ID:164471
 
Im trying to develop my first game and i was wondering if anyone could point me in the way for any of the following please, thanks very much.
-Skill gain
-Skill level requirements
-Skill item requirements
-Float layers
-Sturdy attacking system
-Magic/Ranged

I have looked through the demos but nothing seems to work. Im not trying to have everything handed to me either, i just wanted to see if anyone could help
There is a demo called skillgain..... I heard it's really good. Oh and if you haven't even done ANYTHING on DM, Zilal's tutorial rocks!
flocSkill gain is simple: just set up a variable, and use it. What matters is what you do with that variable.

mob
var
swords = 0
axes = 0
maces = 0


Now, as for the equipment system, and skill requirements, here's a basic way of doing it:

mob
var
swords = 0
axes = 0
maces = 0
obj/item/equipment/weapon/weapon //variable of type obj/item/equipment/weapon, named "weapon"
basedamage = 1
baseattackspeed = 10
damage = 1
attackspeed = 10

obj
item
verb
get()
set src in oview(1)
if(Move(usr)) usr << "You get [src]"
drop()
set src in usr
if(Move(usr.loc)) usr << "You drop [src]"
equipment
verb
equip()
set src in usr
//defined by children
weapon
var/damage
var/attackspeed
var/skillrequired = 0
equip()
set src in usr
if(usr.weapon != src) //If this weapon isn't the one equipped
usr.weapon = src //Then equip it
usr.damage = src.damage + usr.basedamage //and add its stats to the player's
usr.attackspeed = src.attackspeed
else //but if it IS equipped
usr.weapon = null //Unequip it
usr.damage = usr.basedamage //and return the player's stats to their base values
usr.attackspeed = usr.baseattackspeed
sword
equip() //However, each weapon type equips things a bit differently, so we override equip()
set src in usr
if(usr.swords >= skillrequired) //First, it checks to see if you can use the sword
..() //and if you can, ..() is called. ..() is calling the parent
//function, which can be thought of as what would have been
//called, had we not overridden it. So the parent of
//obj/item/equipment/weapon/sword/equip() is
//obj/item/equipment/weapon/equip(). This will equip the sword
training_sword //and here we have two instances of specific swords. First, a training sword,
skillrequired = 0 //which requires no skill, but is weak
damage = 1
attackspeed = 5
shortsword //and then a shortsword, which requires 10 skill, but is more powerful
skillrequired = 10
damage = 2
attackspeed = 5
axe //and then maces and axes, which work the same as swords...
equip()
set src in usr
if(usr.axes >= skillrequired) ..() //except that axes is checked instead of swords
training_axe
damage = 2
attackspeed = 10
broadaxe
damage = 4
attackspeed = 10
mace
equip()
set src in usr
if(usr.maces >= skillrequired) ..() //and maces instead of axes or swords
training_mace
damage = 3
attackspeed = 15
mace
damage = 6
attackspeed = 15


"Basic" is relative. While we have long type paths now, things become a bit more organized. Everything that's an item can be picked up and dropped. Everything that's equipment can be equipped. Everything that's a weapon can also be equipped (but, more specifically, it becomes your weapon). Everything that's a sword requires sword skill (even if the requirement is 0).

One thing I neglected, however, is not being able to drop equipped items. A pretty straightforward requirement, of course. But, because I use Move() in get() and drop(), we can make things a bit easier for us. Move() will call Exit(), Enter(), Exited(), and Entered(). Exit() and Enter() need to both return 1 before anything can move out or in. So, we can change mob/Exit() to prevent people from dropping their weapon:

mob
Exit(atom/movable/A)
if(A == weapon) return 0
else return ..()


Or, alternatively, automatically unequip it:

mob
Exit(atom/movable/A)
if(A == weapon)
weapon.equip()
return ..()


The more experienced of you will notice a problem right now: equip() is being used as a proc, but it has usr all over it. Because usr should not be used in procs, we have a conundrum. There are two simple solutions, though:

1) create seperate equipping/unequipping proc(s)
2) make equip() proc-friendly

We'll go with 2. Because we know that the weapon has to be in usr (set src in usr) when using it as a verb, we know that the weapon's loc will always be the player, when being used as a verb. Similarly, because you can only equip weapons you have in your inventory, the loc of the weapon will also always be the player. So, we can use loc instead of player. However, because loc will be of type atom, we have to define a new variable of type mob, and set it equal to loc (and make sure it IS a mob, because it always SHOULD be)

equip()
set src in usr
var/mob/M = loc
if(!istype(M)) return //Sanity check: a non-mob shouldn't ever be trying to equip something,
//but better safe than sorry. Make sure we're in a mob
if(M.weapon != src) //If this weapon isn't the one equipped
M.weapon = src //Then equip it
M.damage = src.damage + M.basedamage //and add its stats to the player's
M.attackspeed = src.attackspeed
else //but if it IS equipped
M.weapon = null //Unequip it
M.damage = M.basedamage //and return the player's stats to their base values
M.attackspeed = M.baseattackspeed


Now, we have a bunch of information, but what we're lacking is an actual combat SYSTEM. What use is damage and attack speed without a way of using them?

mob
var
health = 10
mob/target //variable of type mob, named "target"
proc
attackloop(var/mob/M)
while(target && target == M) //while we have a target, and it hasn't changed
if(target in oview(src, 1)) //if we're close enough to hit them...
attack(target) //hit them!
sleep(attackspeed) //then, wait for our next attack attempt to try to make a swing
attack(var/mob/victim)
var/hurtination = rand(0,damage)
src << "You hit [victim] for [hurtination]!"
victim.ouch(src, hurtination)
ouch(var/mob/meanie, var/hurt)
if(meanie) src << "[meanie] hit you for [hurt]" //if() check because sometimes, we may want unknown
else src << "You take [hurt] damage!" //sources of damage
health -= hurt
deathcheck(meanie)
deathcheck(var/mob/murderer)
if(health <= 0)
if(murderer) world << "[src] has been killed by [murderer]!" //Same deal here
else world << "[src] dies of natural causes."
del(src)
getTarget(var/mob/M)
if(M != src) //attacking yourself is bad form
target = M //get the target
spawn() attackloop(M) //and start attacking it
Click()
usr.getTarget(src)


If you play World of Warcraft, the system is ripped from it. Click on a mob to start attacking it, move in range to actually hit it.

And, of course, we need something to hit. So let's set up a couple monsters, as well as the player type.

    player
die(var/mob/murderer) //Players shouldn't be deleted when they die, they should just be relocated
if(murderer) world << "[src] has been killed by [murderer]!" //Same deal here
else world << "[src] dies of natural causes."
loc = null //Let them stew nowhere for a bit
sleep(10)
health = maxhealth
Move(locate(/area/)) //Then put them back in. We'll just put them in the first available slot in
//whatever area we find.
monster
New()
..()
spawn() AILoop()
proc
AILoop() //alez oup!
while(!client) //Keep on looping forever. However, in the unlikely case that a client is in control
//of a bug, stop the AI so that they can control it.
if(!target) //If we don't have a target...
getTarget(locate(/mob/player/) in oview(5, src)) //get one!
else //otherwise...
if(target in oview(5, src)) //make sure we can still see them
step_towards(src, target) //and take a step towards them
else //but if we've lost them
target = null //forget about them
sleep(movespeed) //between each step, wait an appropriate amount of time
bug //Very buggy
health = 5
movespeed = 2
basedamage = 1
damage = 1
baseattackspeed = 5
attackspeed = 5
goblin
health = 7
movespeed = 3
basedamage = 1
damage = 1
baseattackspeed = 10
attackspeed = 10
New()
..()
//When we make a goblin, let's give them a weapon to use. First, pick the type:
var/weapontype = pick(/obj/item/equipment/weapon/sword/training_sword, \
/obj/item/equipment/weapon/axe/training_axe, \
/obj/item/equipment/weapon/mace/training_mace)
//The \ is used to ignore the line break, so we can turn one very long line of code
//into three shorter ones
var/obj/item/equipment/weapon/W = new weapontype(src) //give it to the goblin
W.equip() //then have it equip it


Now, because I kept on making changes as I wrote this up, some of these blocks of code might be different from what my final version is. So, at the risk of a horde of kids copying-and-pasting it (which, I suppose, is better than the alternative of them copying-and-pasting something else, something else SINISTER), I'll just plop the whole thing down here:

mob
var
movespeed = 3
health = 10
maxhealth = 10
mob/target //variable of type mob, named "target"
swords = 0
axes = 0
maces = 0
obj/item/equipment/weapon/weapon //variable of type obj/item/equipment/weapon, named "weapon"
basedamage = 1
baseattackspeed = 10
damage = 1
attackspeed = 10
Exit(atom/movable/A)
if(A == weapon) return 0
else return ..()
proc
attackloop(var/mob/M)
while(target && target == M) //while we have a target, and it hasn't changed
if(target in oview(src, 1)) //if we're close enough to hit them...
attack(target) //hit them!
sleep(attackspeed) //then, wait for our next attack attempt to try to make a swing
attack(var/mob/victim)
var/hurtination = rand(0,damage)
src << "You hit [victim] for [hurtination]!"
victim.ouch(src, hurtination)
ouch(var/mob/meanie, var/hurt)
if(meanie) src << "[meanie] hit you for [hurt]" //if() check because sometimes, we may want unknown
else src << "You take [hurt] damage!" //sources of damage
health -= hurt
deathcheck(meanie)
deathcheck(var/mob/murderer)
if(health <= 0)
die(murderer)
die(var/mob/murderer)
if(murderer) world << "[src] has been killed by [murderer]!" //Same deal here
else world << "[src] dies of natural causes."
del(src)
getTarget(var/mob/M)
if(M != src) //attacking yourself is bad form
target = M //get the target
spawn() attackloop(M) //and start attacking it
Click()
usr.getTarget(src)

player
die(var/mob/murderer) //Players shouldn't be deleted when they die, they should just be relocated
if(murderer) world << "[src] has been killed by [murderer]!" //Same deal here
else world << "[src] dies of natural causes."
loc = null //Let them stew nowhere for a bit
sleep(10)
health = maxhealth
Move(locate(/area/)) //Then put them back in. We'll just put them in the first available slot in
//whatever area we find.
monster
New()
..()
spawn() AILoop()
proc
AILoop() //alez oup!
while(!client) //Keep on looping forever. However, in the unlikely case that a client is in control
//of a bug, stop the AI so that they can control it.
if(!target) //If we don't have a target...
getTarget(locate(/mob/player/) in oview(5, src)) //get one!
else //otherwise...
if(target in oview(5, src)) //make sure we can still see them
step_towards(src, target) //and take a step towards them
else //but if we've lost them
target = null //forget about them
sleep(movespeed) //between each step, wait an appropriate amount of time
bug //Very buggy
health = 5
movespeed = 2
basedamage = 1
damage = 1
baseattackspeed = 5
attackspeed = 5
goblin
health = 7
movespeed = 3
basedamage = 1
damage = 1
baseattackspeed = 10
attackspeed = 10
New()
..()
//When we make a goblin, let's give them a weapon to use. First, pick the type:
var/weapontype = pick(/obj/item/equipment/weapon/sword/training_sword, \
/obj/item/equipment/weapon/axe/training_axe, \
/obj/item/equipment/weapon/mace/training_mace)
//The \ is used to ignore the line break, so we can turn one very long line of code
//into three shorter ones
var/obj/item/equipment/weapon/W = new weapontype(src) //give it to the goblin
W.equip() //then have it equip it

client
var/nextmove = 0 //This keeps track of when next we're allowed to move
Move()
if(world.time < nextmove) //if it's not our time to move yet,
return 0 //do nothing
else //but if it is
nextmove = world.time + mob.movespeed //set the time we can move next
return ..() //then move

obj
item
verb
get()
set src in oview(1)
if(Move(usr)) usr << "You get [src]"
drop()
set src in usr
if(Move(usr.loc)) usr << "You drop [src]"
equipment
verb
equip()
set src in usr
//defined by children
weapon
var/damage
var/attackspeed
var/skillrequired = 0
equip()
set src in usr
var/mob/M = loc
if(!istype(M)) return //Sanity check: a non-mob shouldn't ever be trying to equip something,
//but better safe than sorry. Make sure we're in a mob
if(M.weapon != src) //If this weapon isn't the one equipped
M.weapon = src //Then equip it
M.damage = src.damage + M.basedamage //and add its stats to the player's
M.attackspeed = src.attackspeed
else //but if it IS equipped
M.weapon = null //Unequip it
M.damage = M.basedamage //and return the player's stats to their base values
M.attackspeed = M.baseattackspeed
sword
equip() //However, each weapon type equips things a bit differently, so we override equip()
set src in usr
var/mob/M = loc
if(!istype(M)) return
if(M.swords >= skillrequired) //First, it checks to see if you can use the sword
..() //and if you can, ..() is called. ..() is calling the parent
//function, which can be thought of as what would have been
//called, had we not overridden it. So the parent of
//obj/item/equipment/weapon/sword/equip() is
//obj/item/equipment/weapon/equip(). This will equip the sword
training_sword //and here we have two instances of specific swords. First, a training sword,
skillrequired = 0 //which requires no skill, but is weak
damage = 1
attackspeed = 5
shortsword //and then a shortsword, which requires 10 skill, but is more powerful
skillrequired = 10
damage = 2
attackspeed = 5
axe //and then maces and axes, which work the same as swords...
equip()
set src in usr
var/mob/M = loc
if(!istype(M)) return
if(M.axes >= skillrequired) ..() //except that axes is checked instead of swords
training_axe
damage = 2
attackspeed = 10
broadaxe
damage = 4
attackspeed = 10
mace
equip()
set src in usr
var/mob/M = loc
if(!istype(M)) return
if(M.maces >= skillrequired) ..() //and maces instead of axes or swords
training_mace
damage = 3
attackspeed = 15
mace
damage = 6
attackspeed = 15


Now, I realize this isn't addressing all of your questions. However, working from what I've given you, there's a very easy way to make a ranged weapon (it involves changing the attackloop proc, as well as adding a range variable to weapons). Making a goblin that'll use a ranged weapon is trickier: the AILoop() is going to have to figure out what the goblin's using, and act accordingly. Finally, skill gain is the trickiest, because it's a vague idea. Best I can say is that you create a proc for weapons, swingweapon(). Then, it'll be overridden for swords and axes and maces. In there, it will possibly increase the skill. Then, call weapon.swing() in the attack() proc (after checking to make sure weapon isn't null, of course).

Other issues is that the AI routine is a bit dull. They could take a step randomly when they don't see someone. However, best of all would be for them to completely freeze when nobody is looking. No point doing things when nobody is around to see it. However, this means that when a player moves around, they'll need to check for enemies nearby, and activate their AI routine. This is tricky, of course, and you shouldn't NEED to worry about it unless if you have a lot of monsters to worry about.

[edit]Forgot to change obj/item/equipment/weapon/sword/equip() (and axe and mace) to not use usr. Fixed that.

[edit2]Whoops! "locate(target)" should've just been "target." In addition, the call to attackloop() needed to be spawn()ed off. These kept the AILoop() from actually working. I fixed them. In addition, the AILoop() should've been spawned off (this was keeping goblins from getting weapons). Finally, Move(null) doesn't work. It should be loc = null.