REM RGBRPG, created by Tinkergirl and initially inspired by RiiDii's tutorial
REM on the DarkBasicPro Challenge thread.
 
`Create the player type, this is the collection of stats for the player.
Type Character
   Rage as Float
   Grace as Float
   Brains as Float
   MaxHP as Float
   HP as Float
   Money as Float
   Level as Float
   XP as Float
   Name as String
   Object as Integer
EndType
 
`Declare our player as a global variable.
Global Player as Character
 
`Create our baddie (enemy) type - we'll make several of these.
Type Baddie
   Rage as Float
   Grace as Float
   Brains as Float
   HP as Float
   XPDrop as Integer
   MoneyDrop as Integer
   Name as String
   Object as Integer
Endtype
 
Dim Enemies(10) as Baddie
 
global Fighting as Integer
 
sync on: sync rate 60
randomize timer()
 
FirstTimeThrough = 0
Player.Object = 1
Fighting = 0
global Loopy as integer
global Attack as integer
Loopy = 400
Global AttackName$ as String
global DamageFloat as Integer
global Combatant as Integer
global damage as Integer
global FloatTime as Integer
 
Do
 
   if FirstTimeThrough = 0
      NameCharacter()   `asks player for a name, keeps it.
      StatCharacter()   `creates random stats and either rerolls or keeps.
      GenerateBaddie()  `Might work.
      MakeCube()        `makes our player object, colours it etc.
      MakeWorld()       `creates the floor plain, lights etc.
      MakeLevel()       `generates the buildings/walls of our environment
      MakeFloater()     `Make the plain that shows damage.
      FirstTimeThrough = 1
      sync on
   endif
 
   if Fighting =0
      CubeMovement()    `controls the movement of our cube character.  WASD keys.
   Endif
   ThirdPerson()        `positions the camera in a 3rd-person style position.
   DrawStats()
 
   if Object Collision(1,0)>49
      if Object Collision(1,0)<100
         Combatant = Object Collision(1,0)-50
         DrawBaddieStats()
         DoFight()
     endif
   endif
 
   if Player.XP>100*Player.Level
      Level()
   endif
 
   if DamageFloat>0
      FloatTime=250
      DamageFloat = 0
   endif
 
      DamageFloater()
 
 
   Sync
Loop
 
 
 
`Ask for a name for the character.  This happens only once.
Function NameCharacter()
   sync off
   `Now, create our character.
   CLS
   Set Cursor 0,0
   Input "Enter your cube's name:",Player.Name
   Print
   Print "Welcome ";Player.Name;" the Cube."
   Wait 1000
Endfunction
 
 
`Create initial stats for the character.  Currently random.  *Something to work on later.
Function StatCharacter()
   do
      Cls
      Set Cursor 0,0
      Print Player.Name;", press [R] to re-roll stats, or [Y] to accept stats."
      Print
 
      `100 stat points initially.  Previous method sometimes resulted in Brains=-1 (ironic, really)
      `So we've set a 'points left' and made sure each stat is always at least 1.
 
      Player.Rage=1
      Player.Grace=1
      Player.Brains=1
      AmountLeft=97
 
      Player.Rage=Player.Rage+rnd(AmountLeft)
      AmountLeft=AmountLeft-Player.Rage+1
      Player.Grace=Player.Grace+rnd(AmountLeft)
      AmountLeft=AmountLeft-Player.Grace+1
      Player.Brains=Player.Brains+AmountLeft
 
      Player.HP=Player.Rage+Player.Grace+Player.Brains
      Player.MaxHP=Player.HP
      Player.Money=(Player.Brains+Player.Grace)*2
      Player.Level=1
      `Still not got a good name or system for assigning the main unit of 'barter'.  Depends.
 
      `Print out the stats - note, Rage, Grace, and Brains correspond to Red, Green and Blue
      `respectively.  This will help people figure out the relative skills of an enemy, etc.
      Ink RGB(255,0,0),0
      Print "(R)age: ";Player.Rage
      Ink RGB(0,255,0),0
      Print "(G)race: ";Player.Grace
      Ink RGB(0,0,255),0
      Print "(B)rains: ";Player.Brains
      Ink RGB(255,255,255),0
      Print "Health: ";Player.HP;"/";Player.MaxHP
      Print "Monetary Units: ";Player.Money
 
      Ink RGB(INT(Player.Rage),INT(Player.Grace),INT(Player.Brains)),0
      Box 180,30,260,110
      Ink RGB(255,255,255),0
      `Let the player choose to keep the stats or re-roll.
 
      Do
         a$=Lower$(Inkey$())
         If a$="r" or a$="y"
            wait 300
            Exit
         endif
      Loop
 
      if a$="y" Then Exit
   loop
Endfunction
 
 
`Make our cubic avatar
Function MakeCube()
 
   make object cube Player.Object,Player.HP/10
   color object Player.Object, rgb(INT(Player.Rage),INT(Player.Grace),INT(Player.Brains))
   color backdrop 0
   make object collision box Player.Object,-7,-7,-7,7,7,7,0
 
EndFunction
 
 
`This creates our world (currently a plain object - bah)
Function MakeWorld()
 
   autocam off
   make object plain 4,800,800
   position object 4,0,0,0
   rotate object 4,-90,0,0
   color object 4,rgb(50,50,50)
   set object collision off 4
 
   color ambient light rgb(20,20,20)
   autocam on
 
EndFunction
 
 
`Move around using the WASD keys
Function CubeMovement()
 
   `Originally ripped from the monster hunt tutorial, changed most of it to allow vectors and rolling as well as WASD.
   CubeAngle#= Object Angle Y(Player.Object)
   PosX#=Object Position X(Player.Object)
   PosY#=Object Position Y(Player.Object)
   PosZ#=Object Position Z(Player.Object)
 
   `Keys for controlling the object.  WASD standard.
   If Keystate(17)=1  : `"W" key for forward.
     ` WKey = 1
      CubeSpeed# = 1.5
 
      CubeMoveX# = -sin(-Object Angle Y(Player.Object)) * CubeSpeed#
      CubeMoveZ# = cos(-Object Angle Y(Player.Object)) * CubeSpeed#
 
      `Update the positions in the X and Z using the vector.
      PosX#=PosX#+CubeMoveX#
      PosZ#=PosZ#+CubeMoveZ#
 
      `Put a bit of a 'rolling' tumble on it, so we know the cube's moving.
      CurrentXRot#=Object Angle X(Player.Object)
      Xrotate Object Player.Object,CurrentXRot#+5
   else
      WKey = 0
   Endif
 
   If Keystate(31)=1  : `"S" key for reverse.
      CubeRevSpeed# = 1.0
 
      CubeRevMoveX# = -sin(-Object Angle Y(Player.Object)) * CubeRevSpeed#
      CubeRevMoveZ# = cos(-Object Angle Y(Player.Object)) * CubeRevSpeed#
 
      `Again, update the X and Z with the reversing vector.
      PosX#=PosX#-CubeRevMoveX#
      PosZ#=PosZ#-CubeRevMoveZ#
 
      `Put a bit of a 'rolling' tumble on it, so we know the cube's reversing.
      CurrentXRot#=Object Angle X(Player.Object)
      Xrotate Object Player.Object,CurrentXRot#-3
 
   Endif
 
   If Keystate(30)=1 then Yrotate Object Player.Object, Wrapvalue(CubeAngle#-1.5)  :`"A" for left.
 
   If Keystate(32)=1 then Yrotate Object Player.Object, Wrapvalue(CubeAngle#+1.5)  :`"D" for right.
 
   `Object collision (Want's it's own function soon, I believe.)
   if object collision (1,5) > 0
      dec PosX#,get object collision x()
      dec PosY#,get object collision y()
      dec PosZ#,get object collision z()
   endif
 
   `Apply all the position changes last, including collision.
   position object Player.Object, PosX#,4.5+(Player.Level/2),PosZ#
 
EndFunction
 
 
`TEST function to see what keys are being pressed and print their scancode.
Function TestKeys()
   Set Cursor 0,0
   Print SCANCODE()
 
EndFunction
 
 
`The camera control function to position it above and behind our cube.
Function ThirdPerson()
 
   `Special thanks to my bf for showing me his DarkSDK code that he wrote for 3rd Person.  Needed modifying to work.
   CameraOffset# = 50
 
   CameraHeight = Object Position Y(Player.Object)+30
 
   OffsetX# = -sin(-Object Angle Y(Player.Object)) * CameraOffset#
   OffsetZ# = cos(-Object Angle Y(Player.Object)) * CameraOffset#
 
   Position Camera 0, Object Position X(Player.Object)-OffsetX#, CameraHeight , Object Position Z(Player.Object)-OffsetZ#
   Point Camera 0, Object Position X(Player.Object), Object Position Y(Player.Object)+15, Object Position Z(Player.Object)
 
EndFunction
 
 
`Create the walls of our level/maze/city whatever.
Function MakeLevel()
 
   make object box 5,100,20,20
   make object collision box 5,-50,-10,-10,50,10,10,0
   position object 5, 150,0,120
 
Endfunction
 
 
`Try to generate some randomised baddies.  This will need a lot of work (especially placement).
Function GenerateBaddie()
 
   for i = 0 to array count(Enemies())
 
      Enemies(i).Rage=rnd(50)
      Enemies(i).Grace=rnd(50)
      Enemies(i).Brains=rnd(50)
 
     Enemies(i).HP=(Enemies(i).Rage+Enemies(i).Grace+Enemies(i).Brains)
 
      Enemies(i).XPDrop=(Enemies(i).Rage+Enemies(i).Grace+Enemies(i).Brains)/3
      Enemies(i).MoneyDrop=Enemies(i).XPDrop/2
 
      `Enemies(i).Name="EvilBall"
      if Enemies(i).Rage>Enemies(i).Grace
         if Enemies(i).Rage>Enemies(i).Brains
            Enemies(i).Name="Bezerker Ball"
         else
            Enemies(i).Name="Brainy Ball"
         endif
      else
         if Enemies(i).Grace>Enemies(i).Brains
            Enemies(i).Name="Ninja Ball"
         else
            Enemies(i).Name="Brainy Ball"
         endif
      endif
 
      Enemies(i).Object=50+i
 
      Make Object Sphere Enemies(i).Object,(Enemies(i).HP)/6
      Set Object Collision to Spheres Enemies(i).Object
      Set Object Collision on Enemies(i).Object
     ` Set Shadow Shading on Enemies(i).Object
      Position Object Enemies(i).Object,rnd(600)-300,Enemies(i).HP/12,rnd(600)-300
      Color Object Enemies(i).Object,rgb(Enemies(i).Rage,Enemies(i).Grace,Enemies(i).Brains)
   next i
 
Endfunction
 
`Draw the players stats in terms of colours in the top left of the screen.
Function DrawStats()
 
   set cursor 1,1
   Print Player.Name," The Lv.",Player.Level," Cube"
 
   ink rgb(255,0,0),0
   set cursor 1,17
   Print "Rage:"
   box 5,33,Player.Rage+5,43
 
   ink rgb(0,255,0),0
   set cursor 1,43
   Print "Grace:"
   box 5,58,Player.Grace+5,68
 
   ink rgb(0,0,255),0
   set cursor 1,68
   Print "Brains:"
   box 5,83,Player.Brains+5,93
 
   ink rgb(255,255,255),0
   set cursor 1,94
   Print "HP:"
   ink rgb(INT(Player.Rage),INT(Player.Grace),INT(Player.Brains)),0
   box 3,108,Player.MaxHP+7,120
   ink rgb(255,255,255),0
   box 5,109,Player.HP+5,119
 
   if keystate(18)=1
      set cursor 1,124
      Print "XP: ",Player.XP
      set cursor 1,138
      Print "Money: ",Player.Money
   endif
EndFunction
 
 
`Draw the stats of the baddies when you collide with them.  Hopefully, this will trigger when a fight starts.
Function DrawBaddieStats()
 
   Collider= Combatant
 
   set cursor screen width()-Text Width (Enemies(Collider).Name)-Text Width (" The Enemy!"),1
   Print Enemies(Collider).Name," The Enemy!"
 
   ink rgb(255,0,0),0
   set cursor screen width()-Text Width("Rage:"),17
   Print "Rage:"
   box screen width()-Enemies(Collider).Rage-5,33,screen width()-5,43
 
   ink rgb(0,255,0),0
   set cursor screen width()-Text Width ("Grace:"),43
   Print "Grace:"
   box screen width()-Enemies(Collider).Grace-5,58,screen width()-5,68
 
   ink rgb(0,0,255),0
   set cursor screen width()-Text Width("Brains:"),69
   Print "Brains:"
   box screen width()-Enemies(Collider).Brains-5,84,screen width()-5,94
 
   ink rgb(255,255,255),0
 
   set cursor screen width()-Text Width("HP:"),95
   Print "HP:"
   box screen width()-Enemies(Collider).HP-5,110,screen width()-5,120
 
EndFunction
 
 
`Fight function - this is the meat of the gameplay.  Press 1,2 or 3 to choose an attack.
Function DoFight()
 
   Fighting = 1
 
   ConSystemInk()
   center Text screen width()/2,screen height()-65,"Fight"
   ink rgb(255,255,255),0
 
   BattleTimer()
 
   CombatCamera()
 
 
`Some placeholder attack types and names.
 
If Keystate(2) = 1 :`Key 1
   if Player.Rage>5
      Attack = 1
      AttackName$ = "Rage Frenzy"
   endif
endif
 
If Keystate(4) = 1 :`Key 3
   if Player.Grace>5
      Attack = 2
      AttackName$ = "Stealth Stab"
   endif
endif
 
If Keystate(7) = 1 :`Key 6
   if Player.Brains>40
      Attack = 3
      AttackName$ = "Emotional Vampirism"
   endif
endif
 
if Keystate(6) = 1 :`Key 5
   if Player.Brains>5
      Attack = 4
      AttackName$ = "Mind Heal"
   endif
endif
 
if Keystate(5) = 1 :`Key 4
   if Player.Grace>40
      Attack = 5
      AttackName$ = "Reactive Momentum"
   endif
endif
 
if Keystate(3) = 1 :`Key 2
   if Player.Rage>40
      Attack = 6
      AttackName$ = "All Or Nothing"
   endif
endif
 
   if Attack > 0
      ink 0,0
      center Text screen width()/2,screen height()-45,AttackName$
      ink rgb(255,255,255),0
   endif
 
   if Loopy=0
      if Attack>0
         if object exist(Object Collision(1,0))
            eneX=Object Position X(Enemies(Object Collision(1,0)-50).Object)
            eneY=Object Position Y(Enemies(Object Collision(1,0)-50).Object)
            eneZ=Object Position Z(Enemies(Object Collision(1,0)-50).Object)
         endif
         DamageBaddie()
      endif
   endif
 
 
   if DamageFloat=500
      show object 300
      position object 300,eneX,eneY+5,eneZ
      point object 300,camera position X(),camera position Y(),camera position Z()
      DamageFloat=499
   endif
 
 
EndFunction
 
 
`Kills the baddie and gives goodies to the player.
Function KillBaddie()
 
   Fighting = 0
   Loopy = 400
   Player.XP = Player.XP + Enemies(Combatant).XPDrop
   Player.Money = Player.Money + Enemies(Combatant).MoneyDrop
   delete object Object Collision(1,0)
EndFunction
 
 
`When in combat, the character (and thus camera) swings around to look at the enemy.
Function CombatCamera()
 
XDiff = Object Position X(Object Collision(1,0)) - Object Position X(1)
ZDiff = Object Position Z(Object Collision(1,0)) - Object Position Z(1)
 
angle# = ATanFull(XDiff, ZDiff)
 
angle# = WrapValue(angle#)
 
AngleDiff# = Object Angle Y(1) - angle#
 
while (AngleDiff# < -180)
   AngleDiff# = AngleDiff# + 360
endwhile
 
while (AngleDiff# > 180)
   AngleDiff# = AngleDiff# - 360
endwhile
 
YRotate Object 1,Object Angle Y(1) - (AngleDiff#/20)
 
EndFunction
 
 
`Depending on the relative average skill levels of your opponent, change the ink colour.  A bit late, but never mind.
Function ConSystemInk()
 
BaddieID = Combatant
 
`RED
if Enemies(BaddieID).Rage+Enemies(BaddieID).Grace+Enemies(BaddieID).Brains > (Player.Rage+Player.Grace+Player.Brains)
   ink rgb(255,0,0),0
else
 
   `YELLOW
   if Enemies(BaddieID).Rage+Enemies(BaddieID).Grace+Enemies(BaddieID).Brains > ((Player.Rage+Player.Grace+Player.Brains)*0.6)
      ink rgb(255,255,0),0
   else
 
   `BLUE
      if Enemies(BaddieID).Rage+Enemies(BaddieID).Grace+Enemies(BaddieID).Brains > ((Player.Rage+Player.Grace+Player.Brains)*0.4)
         ink rgb(0,0,255),0
      else
 
         `PALE BLUE
         if Enemies(BaddieID).Rage+Enemies(BaddieID).Grace+Enemies(BaddieID).Brains < ((Player.Rage+Player.Grace+Player.Brains)*0.4)
            ink rgb(180,180,255),0
         endif
      endif
   endif
endif
 
EndFunction
 
`To increase the characters stats and level when they gain enough XP.
Function Level()
 
Player.Level = Player.Level+1
Player.Rage = Player.Rage+rnd(10)+5
Player.Grace = Player.Grace+rnd(10)+5
Player.Brains = Player.Brains+rnd(10)+5
 
Player.MaxHP = Player.MaxHP+rnd(10)+10
Player.HP = Player.MaxHP
 
Scale Object 1,Player.MaxHP,Player.MaxHP,Player.MaxHP
 
Color Object 1,rgb(Player.Rage,Player.Grace,Player.Brains)
 
Player.XP=Player.XP-(100*(Player.Level-1))
 
EndFunction
 
 
`Battle Timer should let the player do one attack over a length of time.
Function BattleTimer()
if Loopy = 0
   Loopy = 400
endif
if Loopy>0
   Loopy = Loopy - 1
endif
 
if Loopy > 250
   ink rgb(0,255,0),0
   else
      if Loopy > 100
         ink rgb(255,255,0),0
      else
         if Loopy > 50
            ink rgb(255,128,0),0
         else
            if Loopy < 51
               ink rgb(255,0,0),0
            endif
         endif
      endif
endif
 
box (screen width()/2)-Loopy/2,screen height()-45,(screen width()/2)+Loopy/2,screen height()-30
ink rgb(255,255,255),0
 
EndFunction
 
`Instead of killing the baddie in one hit, lets do him some damage.  The numbers are unbalanced.
Function DamageBaddie()
 
BaddieID = Combatant
 
`Rage Frenzy
If Attack = 1
   damage = Player.Rage
   Enemies(BaddieID).HP = Enemies(BaddieID).HP - damage
   set cursor screen width()/2,screen height()/2
endif
 
`Stealth Stab
If Attack = 2
   damage = (Player.Grace/2)*rnd(3)
   if damage <1 then damage=1
   Enemies(BaddieID).HP = Enemies(BaddieID).HP - damage
   set cursor screen width()/2,screen height()/2
endif
 
`Emotional Vampirism
If Attack = 3
   damage =(Player.Brains)/1.5
   if damage <1 then damage=1
   Enemies(BaddieID).HP = Enemies(BaddieID).HP - damage
   Player.HP=Player.HP+damage
   if Player.HP>Player.MaxHP then Player.HP=Player.MaxHP
   set cursor screen width()/2,screen height()/2
endif
 
`Mind Heal
if Attack = 4
   damage = 1
   Player.HP = Player.HP + (Player.Brains*2)
   if Player.HP>Player.MaxHP
      Player.HP=Player.MaxHP
   endif
endif
 
`Defensive Momentum
if Attack = 5
   damage = Player.Grace/2+Enemies(BaddieID).Grace
   Enemies(BaddieID).HP = Enemies(BaddieID).HP - damage
endif
 
`All Or Nothing
if Attack = 6
   chance = rnd(2)
   if chance <=1 then damage = 0
   if chance >1 then damage = Player.Rage*2+10
   Enemies(BaddieID).HP = Enemies(BaddieID).HP- damage
endif
 
if Attack > 0
   TextureFloater()
   DamageFloat=500
   Attack = 0
   Player.HP=Player.HP-(Enemies(BaddieID).Rage/2)
endif
 
 
`if Player.HP<1
`   KillPlayer()
`endif
 
if Enemies(BaddieID).HP < 1
   KillBaddie()
endif
 
EndFunction
 
Function MakeFloater()
 
create bitmap 1,32,32
set current bitmap 0
make object plain 300,10,10
set object 300,1,1,0,1,0
hide object 300
 
EndFunction
 
` Draw the appropriate number of the damage on plain 400, the floater.
Function TextureFloater()
 
set current bitmap 1
ink 0,0
box 0,0,32,32
ink rgb(128,0,0),0
circle 16,16,10
circle 16,16,9
ink rgb(255,0,0),0
center text 17,9,STR$(damage)
ink rgb(255,255,255),0
get image 400,0,0,32,32
set current bitmap 0
texture object 300,400
 
EndFunction
 
Function DamageFloater()
 
if FloatTime > 0
   a = object position x(300)
   b# = object position y(300)+0.3
   c = object position z(300)
 
   position object 300,a,b#,c
   point object 300,camera position X(),camera position Y(),camera position Z()
   FloatTime = FloatTime-1
`   print FloatTime
endif
 
if FloatTime = 0
   Hide Object 300
endif
 
 
EndFunction