ID:2796016
 
Code:
#define MOVE_SLIDE 1
#define MOVE_JUMP 2
#define MOVE_TELEPORT 4

#define TILE_WIDTH 32
#define TILE_HEIGHT 32
#define TICK_LAG 0.4

mob/player
appearance_flags = LONG_GLIDE
var
move_delay = 1
tmp
next_move = 0
last_move = 0
move_dir = 0
move_flags = 0

Move(atom/NewLoc,Dir=0)
var/time = world.time
if(next_move>time)
return 0
if(!NewLoc)
move_dir = Dir
move_flags = MOVE_SLIDE
else if(isturf(loc)&&isturf(NewLoc))
var/dx = NewLoc.x - x
var/dy = NewLoc.y - y
if(z==NewLoc.z&&abs(dx)<=1&&abs(dy)<=1)
move_dir = 0
move_flags = MOVE_SLIDE
if(dx>0) move_dir |= EAST
else if(dx<0) move_dir |= WEST
if(dy>0) move_dir |= NORTH
else if(dy<0) move_dir |= SOUTH
else
move_dir = 0
move_flags = MOVE_JUMP
else
move_dir = 0
move_flags = MOVE_TELEPORT
glide_size = TILE_WIDTH / max(move_delay,TICK_LAG) * TICK_LAG
. = ..()
if(.)
next_move = time+move_delay list/__opposite_dirs = list(2,1,null,8,null,null,null,4)

mob/player
var/tmp
input_dir = 0
verb
MoveKey(key as num,state as num)
set hidden = 1
set instant = 1
. = input_dir==0
//update the INPUT_DPAD status
var/opposite = get_opposite(key)
if(state)
//if this is a keypress, turn on the key bit
input_dir |= key
move_dir |= key
//turn off the opposite key bit
if(opposite & input_dir)
move_dir &= ~opposite
else
//if this is a keyrelease, turn off the key bit
input_dir &= ~key
move_dir &= ~key
//turn on the opposite key bit if it's being held
if(opposite & input_dir)
move_dir |= opposite
if(.&&input_dir!=0)
MoveLoop()

proc
MoveLoop()
if(PM && !KO)
while(input_dir)
if(move_dir)
step(src,move_dir)
sleep(world.tick_lag)


Problem description: Good afternoon, I would like to ask you guys here on the forum for help regarding something I would like to implement in my game.

I've been thinking about how, but I still haven't been able to think of a way (maybe it's just lack of knowledge) to put turn based combat in my game.

The way I think is as follows. I would like that when a player attacks another player/mob (and restricted to mobs only), both enter "combat mode" and both have turns to do their actions, each with a certain amount of movement and attack.

If any player attacked them who are already in combat, he would also be part of it. Each with a turn of action. Upon reaching the last, return to the first and so on until the combat is over. The fight would end with only one standing or if everyone agreed to flee. (When a player goes KO, it must be removed from that fight. And there must also be a function for them to try to flee from it.)

For that, I imagine that a proc with a list should be created as soon as one mob deals damage to another. This list is made to include and control who is in combat, and executing on them the limitation of actions in the turn. It should have movement and attack point variables as well, I believe. Can you help me with this?
In case it's necessary to control the players' movements, I'm using the following library for the characters' movement. The code inside this snippet is the movement one i'm using, just in case it is necessary for what i'm asking help to.

This is a good question! It's great to see that you've put a good amount of thought into a possible solution.

If I had to implement a turn-based combat system in my game, I would so something like this:

There is a useful programming concept called a Finite State Machine. People may also refer to them as "finite state automata," depending on the tutorial/guide/video you're using. I personally just call them "state machines."

A "state machine" is a series of "states" that have a clear path between them. This example should be pretty straightforward (forgive the long link):

http://magjac.com/graphviz-visual-editor/?dot=digraph%20%7B% 0A%20%20node%20%5Bstyle%3Dfilled%5D%0A%20%20%0A%20%20new_gam e%20%5Blabel%3D%22New%20game%22%20fontcolor%3D%22white%22%20 fillcolor%3D%22black%22%5D%0A%20%20black_turn%20%5Blabel%3D% 22%E2%99%9FBlack%27s%20turn%22%5D%0A%20%20black_wins%20%5Bla bel%3D%22Black%20wins%22%5D%0A%20%20white_turn%20%5Blabel%3D %22%E2%99%9FWhite%27s%20turn%22%5D%0A%20%20white_wins%20%5Bla bel%3D%22White%20wins%22%5D%0A%20%20game_over%20%5Blabel%3D% 22Game%20over%22%20fontcolor%3D%22white%22%20fillcolor%3D%22 black%22%5D%0A%20%20%0A%20%20new_game%20-%3E%20white_turn%0A %20%20black_turn%20-%3E%20white_turn%20%5Blabel%3D%22Move%20p iece%22%5D%0A%20%20black_turn%20-%3E%20white_wins%20%5Blabel %3D%22Resign%22%5D%0A%20%20white_turn%20-%3E%20black_turn%20% 5Blabel%3D%22Move%20piece%22%5D%0A%20%20white_turn%20-%3E%20 black_wins%20%5Blabel%3D%22Resign%22%5D%0A%20%20white_turn%2 0-%3E%20white_wins%20%5Blabel%3D%22Move%2C%20Checkmate%22%5D %0A%20%20black_turn%20-%3E%20black_wins%20%5Blabel%3D%22Move% 2C%20Checkmate%22%5D%0A%20%20white_wins%20-%3E%20game_over%0 A%20%20black_wins%20-%3E%20game_over%0A%7D

this is a diagram for a chess state machine. Make sure to click on the "fit screen" icon on the top right, between the zoom-in/zoom-out icons and the 1:1 icon. Notice how there is a clear start, and a clear end. Whenever you design your state machines, make sure to always include both of those states.

Also very important: pay attention to how, in a normal game of chess, you can loop around the various states a bunch of times before finally reaching the end of the game. This is important: you will also most likely use some sort of "loop" to control your battle logic.

I'm going to use pokemon as my example for a turn-based combat system. This is a simple diagram for a pokemon battle state machine. Again, make sure to click on the "fit screen" icon on the top right, between the zoom-in/zoom-out icons and the 1:1 icon.

This is a "fleshed out" diagram for my pokemon battle state machine. Third time's the charm, make sure to click on the "fit screen" icon on the top right, between the zoom-in/zoom-out icons and the 1:1 icon. All the nodes on the diagram with the same color are parts of the game code that belong to the same state.

It's not *exactly* what the rules of pokemon are (for example, players cannot try to run; and I designed it with more than 2 players in mind), but it's close enough that we can write some code for it and get some sort of game logic going.

First, I'll create a datum to represent a "pokebattle." A datum is custom code, just like an /obj or a /mob -- in the sense that it can have procs and vars -- except it doesn't have an icon or a place on the map.

Here's the "skeleton code" for that, which includes the start and the end states, as well as some basic data it needs to keep track of, like the players and the pokemon still in battle:
pokebattle
var/list/players = new/list()
var/list/pokemon = new/list()

New(list/players)
// TODO
Del()
// TODO


Now, for every state in my "compact" diagram, I will write a proc that represents it.

pokebattle
var/list/players = new/list()
var/list/pokemon = new/list()

proc/state_check_battle_over()
// TODO
proc/state_check_players_have_pokemon_in_battle()
// TODO
proc/state_pokemon_take_actions()
// TODO
proc/state_add_pokemon_to_battle()
// TODO
New(list/players)
// TODO
Del()
// TODO


Finally, let's actually write the logic for each state.

pokebattle
var/list/players = new/list()
var/list/pokemon = new/list()

// Green state in the diagram.
proc/state_check_battle_over()
if (length(players) <= 1)
// Switch to a different state.
del src
else
// Switch to a different state.
state_check_players_have_pokemon_in_battle()

// Cyan state in the diagram.
proc/state_check_players_have_pokemon_in_battle()
var/list/players_without_pokemon = new/list()
for (var/player/play in players)
var/player_has_pokemon = FALSE
for (var/pokemon/poke in play.party)
if (poke in src.pokemon)
player_has_pokemon = TRUE
break
if (!player_has_pokemon)
players_without_pokemon |= play // The `|=` adds things to a list.

// Switch to a different state.
if (length(players_without_pokemon) > 0)
state_add_pokemon_to_battle(players_without_pokemon)
else
state_pokemon_take_actions()

// Blue state in the diagram.
proc/state_pokemon_take_actions()
for (var/pokemon/poke in pokemon)
unfreeze_player(poke.owner) // This function doesn't exist. You should write it.

/**
* Your custom battle logic goes here.
* Show a UI, play animations, blah blah blah.
*/


// Suppose you attacked, for example...
if (target.hitpoints <= 0)
pokemon -= target

freeze_player(poke.owner) // This function doesn't exist. You should write it.

// Switch to a different state.
state_check_players_have_pokemon_in_battle()

// Red state in the diagram.
proc/state_add_pokemon_to_battle(list/players_without_pokemon)
for (var/player/play in players_without_pokemon)
var/list/pokemon_ready_for_battle = new/list()
for (var/pokemon/poke in play.party)
if (poke.hitpoints > 0)
pokemon_ready_for_battle |= poke // The `|=` adds things to a list.
// Can the player add a pokemon to battle?
if (length(pokemon_ready_for_battle) > 0)
// Add pokemon to battle
var/pokemon/new_poke = input(/* ... */)
pokemon |= new_poke // The `|=` adds things to a list.
else
// Remove player from battle
players -= play

// Switch to a different state.
state_check_battle_over()

// Start state in the diagram.
New(list/players)
src.players |= players // The `|=` adds things to a list.
for (var/player/p in src.players)
freeze_player(p) // This function doesn't exist. You should write it.

// Switch to a different state.
state_check_battle_over()

// End state in the diagram.
Del()
for (var/player/p in src.players)
unfreeze_player(p) // This function doesn't exist. You should write it.

// There is no switching to a different state here.


This is one way to reason about the problem, and this is one way to implement a state machine. I wrote this severely sleep deprived at 5am, and have no idea if it compiles or runs. I'm sure the code has potential bugs too -- like, if you allow a player to swap pokemon during their turn, you have to make sure that the new pokemon they swapped in doesn't act later in the same turn.

In any case, I hope it gives you some guidance, at the very least. Feel free to play around with the code, and keep asking questions.

Note to moderators: When I added the first link as an anchor HTML tag, it got rendered as [a rel="nofollow"] for some reason.
It is possible to do everything by .dmf
buttons, commands, input, labels

But the best way is to use the map
screen_loc, client.screen+= item