Something like delayed fireball, which takes one turn to chargeup and one to
execute, shouldn't be much harder. A callback to execute the attack itself is
scheduled in one turn and the current turn is spent "charging up", which pretty
much does nothing. The 'Hit' event for the actor can be broadcast to the
execute_fireball callback and interrupt it if that's how I decide to handle
that attack.
It gets tough in the case of picking up multiple items. Somebody suggested it
take 96 + (4 * x) ticks (with 100 being a turn) to pick up items. But in my
current system, there are no ticks. I don't want an overwhelming amount of
realism, but the possibility for it. What does 1.04 turns actually mean
practically? My first thought was to have it go towards which actor can go
first in a turn, thus making it a sort of priority queue. But the .04 wouldn't
really go away or change for most normal 1.00 turns until you do another action
with a remainder. Either way, this sort of realism and complexity isn't really
desirable. I was going to implement hasting/slowness by setting the amount of
actions that can be performed in a turn to a higher amount than usual. But then
how does that tie in with priority? Should the player be able to move twice in
a turn? Or should it move once, another monster do something, then another
move? How does the player know that's not a normal turn?
What I could do is have every action performed schedule itself to actually
occur in such and such time units. But that only seems to work for stuff like
a delayed magic attack. I just don't get how I should handle picking up
multiple items. I know there are different ways and I know I've asked this
before, but I still don't get it. Sorry. Can anybody help?
I don't see the problem. In my game I've got a priority queue, which
returns the creature earliest in time. Then every move has some
assigned duration, say 100 for walking, 50 for running, etc.
A faster creature may have a multiplier for this. So after every
move the creature is pushed back into the queue, with updated
time. There are no turns. You can say a turn is 100, but if your
PC moves in 80 units, you will get 5 moves in 4 turns.
Example:
Bat has a speed of 120.
Player has a speed of 100.
Turn: 1 2 3 4 5 6 7 8 9 10
Bat: 120 240 360 480 600 720 840 960 80 200
Player: 100 200 300 400 500 600 700 800 900 0
So we see that the bat moves again at turn 9, but the player can only
act at move 10 (because his speed is lower). Also, partial moves (for
example ones that take up 1.2 turns) are accounted for with the
carryover (1020 -> 20, not 0). You can give different values for
different actions too. This makes it fairly simple to have heavy
weapons take longer to attack with, and to allow for pack weight.
(Speed = Speed - (pack / Str)). If your speed was 100, your pack had
50 pounds in it, and your strength was 10, your speed would be reduced
by 5 (100 - (50 / 10)). You could say that your attack speed is (Speed
- weapon weight). This way you could attack very quickly with light
weapons (knives) or slowly with heavy weapons (hammer). Obviously
heavy weapons would do more damage. Attacking with hammer (weight 20)
would take 13 game turns, instead of the normal 10 for moving. I hope
this system helps.
It seems like everyone is relying on ticks for thier game turns, or
discarding the excess. Is there any way of taking up 1.2 turns without
that? I personally prefer ticks, but if you don't want to go there you
could keep a tally of how much time you gave left over, and when it's a
whole turn, you get an extra turn. That's kind of like ticks though.
other than that I like the OP's current solution, which makes the pc
'hurry' to finish a move, and suffer a stat drain. I like this solution
because stats are basically there to make up for what you can't
simulate. you can't simulate. You can't simulate the partial turn
without a tick system, so use a stat equivalent of not having a full
turn.
OTOH, it's not exactly fair to the player to penalize him for certain
actions. There could be a game option, though: Rush Actions. If it
was on, then all actions would take the same amount of time, but the
player would suffer stat penalties for slow actions (maybe bonuses for
quick ones?). If it was off, then the player would take longer to
finish actions, but would suffer no penalizations. This might be the
best way to implement this.
A thought. Say the player just executed some action and they 0 action
points allocated. When should they be allowed to choose another move?
When their AP reaches some value like 1/100, depending on ratio? What if
they choose to do an action costing 1.5 turns and another character goes
between them? It'd look weird:
> Pick up the bread ration? y
> Pick up the great sword? n
> Pick up the really heavy troll corpse? y
- The goblin thumps you in the head.
You've picked up the items.
The problem isn't an enemy killing them during this delay, as events
sucha s On_hit can interrupt accordingly. It just seems weird. But
still, when should the player be asked for their next move? When the AP
reaches the standard value, i.e. the one that most turns take?
> The players speed is adjusted due to effects (haste, slow, etc) and
> attacks can take up variable amounts of time. Not too much is still
> based on the full turn, but a couple things get triggered every 10
> segments so I guess thats still the definition of a turn.The player
> and each creature can have only one action pending. So I guess they
> can't walk and chew gum at the same time. Not saying it is the best
> or most clever solution (it is not even *my* solution). But it is
> *A* solution. It of course requires something anagolous to ticks
> which I guess you didn't want to have.
>
It sounds decent, but I think I won't use it. I probably will eventually
allow for walking and chewing gun simultaneously. Thanks for the idea
though.
Wait. What's the carryover used for? If you only gain back X amount each
turn and there are no concept of mini-turns (ticks?), then it only
factors in for speed.
I'm all confused again. Thanks though.
A question though. For actions costing 1.5 turns, .5 is the deduction
from the preparedness factor. (That's not the exact number, obviously.)
The same for 1.6, 1.73469 (though I may decide not to get THAT exact),
and 1.9. For something like 1.9, though, should I round up, call it 2
turns (thus forcing the player to lose a turn) and give them a bonus?
It seems like I can either have players lose a turn and get a
preparedness bonus or keep a turn but lose preparedness. It should
probably be a choice, shouldn't it? Keeping a turn sounds more
beneficial. Maybe if I made the preparedness factor equivalent in
fairness to the loss of a turn factor.... I dunno.
Oh yeah, clarification on my project. I'm planning to create several
roguelike games, starting with a stereotypical one to polish my engine,
then experiment more and more. My original idea back in July was to
create something like an Earthbound clone in ASCII art format. Seems
silly now, but I might incorporate more non-random factors into some of
my other games. Multi-parties, online games, and other such rarely-used
ideas might creep their way into my projects, thus the engine will
benefit.
#######
#@....#
#...B.#
#.....#
#.T...#
#.....#
#######
You have the Player ('@') with speed 10
You have a Bat ('B') with speed 14
You have a Troll ('T') with speed 6
The average speed in your game is 10
Moving one mapcell takes 1000 milliseconds (1 second) for a creature
with average speed (ie. 10)
Attacking takes 300 milliseconds (0.3 seconds) for a creature with
average speed (ie. 10)
Then take a normal PQ and insert the Player, Bat and Troll, each with a
priority (time when they get to act next) of 0.
The PQ looks like this:
0 Player
0 Bat
0 Troll
Pop the creature with the lowest priority from the PQ, the Player.
The Player moves east. The time it takes to move one cell for the
Player is ((1000 * 10) / 10) = 1000 ms. Push the Player onto the PQ
again, now with a priority of 0 + 1000. PQ:
0 Bat
0 Troll
1000 Player
Pop the next creature, the Bat. The Bat moves one cell west which takes
((1000 * 10) / 14) = ~714 ms. Push the Bat onto the PQ again with a
priority of 0 + 714. PQ:
0 Troll
714 Bat
1000 Player
Pop the next creature, the Troll. The Troll moves north. This takes
((1000 * 10) / 6) = ~1667 ms. Push the Troll back onto the PQ with a
priority of 0 + 1667. PQ:
714 Bat
1000 Player
1667 Troll
#######
#.@...#
#..B..#
#.T...#
#.....#
#.....#
#######
Pop the next creature, the Bat. The Bat attacks the Player which takes
((300* 10) / 14) = ~214 ms. Push the Bat onto the PQ again with a
priority of 714 + 214. PQ:
928 Bat
1000 Player
1667 Troll
Pop the next creature, the Bat again! Due to it's speed it gets an
extra attack on the player, (((300* 10) / 14) = ~214 ms. Push the Bat
onto the PQ again with a priority of 928 + 214. PQ:
1000 Player
1142 Bat
1667 Troll
Next creature to act is the Player. The Player crushes the bat using a
normal attack, ((300 * 10) / 10) = 300 ms. The Bat dies and is removed
from the PQ. The Player is pushed back onto the PQ with a priority of
1000 + 300. PQ:
1300 Player
1667 Troll
Hope this was easy to follow. I think this is a neat way of solving
when different creatures get to act depending on speed and their
actions. Anything that should be scheduled to act can be put into the
PQ, not only creatures, but also poison effects, timed effects on the
map etc.
/Björn
(posted via Google Groups, *shrug*, any good NNTP servers that allows
posting and are free?)
This is similar to the system I use, as well. There's a global turn
(basically, one time through the main game loop) where each actor is
given a certain amount of energy, which is based on their abilities,
items, etc. There are also two different types of energy - movement and
attack. Each action an actor can perform is classified as either
move-equivalent (basic movement, opening/closing doors) or
attack-equivalent (attacking, casting a spell, using an ability).
Currently, each action requires 1000 of the appropriate energy (but
that will probably change). When an actor attempts to do something, the
appropriate energy pool is checked to see if there's enough energy to
perform the action. Once both of the actor's energy pools drop below
the threshold for an action (1000), their turn has ended, and the next
actor begins their turn. Obviously, the actor can end their turn before
this happens, as well. If an actor does not have enough energy to
perform an action, their turn is simply skipped.
Now, if an actor is given 1500 movement energy on a turn (with an
initial pool of 0), they will be able to perform one move-equivalent
action. Their movement energy will then drop to 500. The next time
their turn comes around, they will have 2000 movement energy, and the
option of taking two move-equivalent actions. So, this actor will be
able to move twice every other turn.
Having everyone on the same turn is nice for the duration of effects,
as you can simply reduce the duration left for all effects by 1 each
turn. I like the separation of movement speed and attack speed, rather
than simply having a 'speed' value, as this opens up options for
different abilities. It also allows for creatures that can attack
quickly (like an actor with 6 arms) but moves slowly, and ones that can
move quickly, but attack at a normal rate. Actors also have the option
(with some restrictions) of using their attack energy for basic
movement. This will let them perform a 'double' move, and simulates the
actor focusing simply on moving.
In my queueing system, there are three separate event queues - one for
"metabolic" effects, one for "physical" effect and one for "intelligence"
effects.
So, a Troll has a fast metabolism, a normal response to physical stimuli,
and a very slow thought-process. We pop off the queues at the relevant
tick, which leads to a transparent ordering of people's turns (the
"intelligence" queing), separate from the ordinary physical and metabolic
effects in the game.
Sam
The thing that's still not entirely clear is whether or not how much
time between two creatures acting matters. I know the bat moves first,
but does it matter that it moves 72 units prior the player? I think it'd
be nice if order was the only factor, but is it OK to disregard how much
the order contrasts specifically?
> Hey. I know this topic has been brought up too many times before, but I just
> don't get it in the way I'm thinking I should. I've already figured out how I'm
> going to handle something like poisoning. Getting poisoned schedules a callback
> to occur in the next turn, which depletes HP and schedules itself for the next
> turn infinitely. (Thus making it viral.) A callback is scheduled to broadcast
> an Unpoisoned event in about 6 turns. But what if a potion of healing is quaffed
> between them? It will broadcast an Unpoisoned event as well. I'll implement a
> dispatcher and watcher system and whatnot, and it will cancel those events.
> (Yes, this isn't really time in the usual sense, but it's cool and it explains
> the callback/event system.)
>
Without looking at the other posts in this rather large topic, here's
how I handled it in my proto-roguelike.
First, you have a turntimer float for each character (PC, NPC,
monster). Then, you set that to 100 / a number based on whatever you're
using for your characters speed.
Next, you run through all the turntimers, pick the lowest number, and
decrease all the turntimers by that number.
Then, all the characters with a zero'd turntimer move and get their
turntimers reset.
It's not truely turn-based, but it does the job.
You could reset everything when the Player moves to a new map.
/Björn - roguelikedevelopment.org
> The thing that's still not entirely clear is whether or not how much
> time between two creatures acting matters. I know the bat moves first,
> but does it matter that it moves 72 units prior the player? I think it'd
> be nice if order was the only factor, but is it OK to disregard how much
> the order contrasts specifically?
I believe so yes. It should be enough.
/Björn
Hmm, yes, you have a point there. You could ofcourse wait with actually
removing the dead Troll until it was the Trolls turn to act again. This
way you could apply any regenerative effects first, then check if HP <=
0 and then remove the Troll if the amount of regeneration wasn't enough
to keep it alive. The message log might look a bit odd though:
The Troll is hit for 5 HP [first monster hits the Troll, HP = 5]
The Troll is hit for 5 HP [second monster hits the Troll, HP = 0]
The Troll crashes to the floor! It looks dead! [instant feedback from
HP reaching 0]
The Troll gets up on its feet again! [the Trolls' turn. Regeneration
kicks in]
or if the Troll actually doesn't regenerate enough HP:
The Troll is hit for 5 HP
The Troll is hit for 5 HP
The Troll crashes to the floor! It looks dead!
The Troll doesn't get up again
Handing out exp for a kill will become a bit more difficult with this
approach, but it's still a possible solution.
Another solution would be to have the Trolls regeneration trigger on
the "onDamage(5 HP) event" regenerating some HP for each damaging hit
the Troll receives.
/Björn - roguelikedevelopment.org
* As much as one can decide. Two days ago, I was confident my stat
* drain/rushed actions system was perfect. Yesterday I was absolutely
* sure an energy/tick system would get the job down. And then today at
* school I realized a priority queue achieves the same effect as an
* energy system (ordering actors correctly) with much less complexity.
* So I don't really know what I'll do for sure yet.
It is a priority queue I think. Everybody starts off with some priority
based on their speed and the smallest is popped off. Every actor's
priority is decreased by that number, ending one or more actors up with
0. They act at 0, then go back in the queue with their original speed
value. I really like this idea, as it prevents the priority from
building up.
How's this compare to the other priority queue idea where everybody
starts at 0, the cost of an action is added to priority each turn, and
the smallest acts again? It seems the same, but there could be
subtle differences.
Format = Actor: Priority - Action
M: 0.9 - Input
P: 1.0 - Input
N: 1.1 - Input
P: 0.1 - Input
N: 0.2 - Input
M: 1.0 - 1.0 Action
N: 0.1 - Input
M: 0.9 - 1.0 Action
P: 1.0 - 1.0 Action
M: 0.8 - 1.0 Action
P: 0.9 - 1.0 Action
N: 1.0 - 1.0 Action
(M performs 1.0 Action and is scheduled for input at 0.9)
P: 0.1 - 1.0 Action
N: 0.2 - 1.0 Action
M: 0.9 - Input
...Etc. The order is: M(I), P(I), N(I), M(A), P(A), N(A)
This system works beautifully. Let me model with 1.5 and 0.8 actions then with
stuff like poison and healing.
Actor: Priority - Action
M: 0.9 - Input
P: 1.0 - Input
N: 1.1 - Input
P: 0.1 - Input
N: 0.2 - Input
M: 1.0 - 1.0 Action
N: 0.1 - Input
M: 0.9 - 1.0 Action
P: 1.5 - 1.5 Action
(N after M because M faster)
M: 0.8 - 1.0 Action
N: 0.8 - 0.8 Action
P: 1.4 - 1.5 Action
(M performs 0.8 Action and is scheduled for input at 0.9)
N: 0.0 - 1.0 Action
P: 0.6 - 1.5 Action
M: 0.9 - Input
(Nothing subtracted since N is already at 0.)
(N performs 1.0 Action and is scheduled for input at 1.1)
P: 0.6 - 1.5 Action
M: 0.9 - Input
N: 1.1 - Input
(P performs 1.5 Action and is scheduled for input at 1.0)
M: 0.3 - Input
N: 0.5 - Input
P: 1.0 - Input
N: 0.2 - Input
P: 0.7 - Input
M: 1.0 - 1.0 Action
P: 0.5 - Input
M: 0.8 - 1.0 Action
N: 1.0 - 1.0 Action
M: 0.3 - 1.0 Action
N: 0.5 - 1.0 Action
P: 1.0 - 1.0 Action
But it seems like N will always be going before P until N does a slow move or P
does a fast move. But isn't this OK? Without turns, as long as the pattern is
somewhat OK, things are fine. It's simply reordered when the P does a slow move
and the N a fast one.
The order ends up being: M(I), P(I), N(I), M(A), N(A), P(A), M(I), N(I), P(I),
M(A), N(A), P(A).... M(A), N(A), P(A)
So the order of actual actions is: M, N, P, M, N, P, M, N, P
This works fine. Eventually it could end up changing to M, P, N.
Even a concept of 'turns' could be invented purely for the sake of the
player. The pattern is M, N, P. That's a turn. Or rather, more simply,
the number of actions the player has been performed is the number of
turns. Purely for the benefit of the player.
Thanks for all the help, guys. I see nothing wrong with this system.
Stuff like healing and poison and other metabolic events will simply be
scheduled as passive. The callbacks won't ask for input or perform an
action already selected, but instead do their job of adding/subtracting
HP or MP. I'm sure I can work it out so the order is fair and something
like a troll won't die unfairly.
Here's what healing does: It checks the monster record to see
how long it's been since the monster last healed, and adds an
appropriate amount of hits for that interval. You heal the
monster on its turn (so that its AI has a reasonable idea of
its health in deciding what to do) and whenever it takes damage
(so you have an accurate idea of how damaged it has become and
whether it is really dead).
In other words, it's a simple rule. You apply healing, like
anything else, on the events where it might make a difference.
Bear
Just to be clear, does this mean that nothing happens when the player
attempts to perform an action without enough energy? Would this
happen every turn as the player gets low on energy for that turn?
Having your keys sometimes perform actions and sometimes do nothing
is likely to seem frustrating and counter-intuitive, I think.
What happens when the player ends a turn before using up the energy?
Does the energy carry on to the next turn, growing larger and larger
until the actor chooses to use it? Probably not, because that would
seem to mean that the player can charge up the PC into a super-mode
by skipping a few turns, then suddenly perform a large number of
actions all within one turn. Taken to the extreme, I imagine the PC
locking himself in a closet and then holding down the 'skip' key for
a million turns to build up energy, then completing the entire game
in the next turn.
The benefit of using a normal priority queue is that you don't have
to iterate over every monster for every turn. You can use a data
structure to increase efficiency. I'm not sure how serious a cost it
might be to scan the monsters and update the priority value, but what
cost is there with using fixed priority values?
The values will have to increase continuously throughout the game, so
we should probably store them in something more powerful than a
normal C++ variable, to allow larger values without limit. It can
also be used as a game-clock to report the length of the game to the
player. Aside from that, there is no cost. I like the idea of having
a very large number of monsters for the PC to face all at once, so
any cost-per-monster concerns me.
> The values will have to increase continuously throughout the game, so
> we should probably store them in something more powerful than a
> normal C++ variable, to allow larger values without limit. It can
> also be used as a game-clock to report the length of the game to the
> player. Aside from that, there is no cost. I like the idea of having
> a very large number of monsters for the PC to face all at once, so
> any cost-per-monster concerns me.
My friend, if I need anything bigger than a Long Double to store
game time, then there must be something very seriously wrong.
okay, maybe after playing a few quintillion turns (which would
take longer than the egyptian empire lasted, give or take) something
that's supposed to take place in a tenth of a turn starts taking place
in 13/128 of a turn instead due to roundoff error... I can live with
it.
Bear
Technically, a five-digit float would be enough for most games - Since
it works on addition and subtraction, the number in there would
probably never be higher than 200 - For slow monsters.