mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
added dead reckoning to the characters
This commit is contained in:
parent
e21b6983ab
commit
33b50738e6
|
@ -105,12 +105,14 @@ Objects = [
|
|||
]),
|
||||
|
||||
NetObject("Character_Core", [
|
||||
NetIntAny("tick"),
|
||||
NetIntAny("x"),
|
||||
NetIntAny("y"),
|
||||
NetIntAny("vx"),
|
||||
NetIntAny("vy"),
|
||||
|
||||
NetIntAny("angle"),
|
||||
NetIntRange("direction", -1, 1),
|
||||
|
||||
NetIntRange("jumped", 0, 3),
|
||||
NetIntRange("hooked_player", 0, 'MAX_CLIENTS-1'),
|
||||
|
@ -125,7 +127,6 @@ Objects = [
|
|||
|
||||
NetObject("Character:Character_Core", [
|
||||
NetIntRange("player_state", 0, 'NUM_PLAYERSTATES-1'),
|
||||
NetIntRange("wanted_direction", -1, 1),
|
||||
NetIntRange("health", 0, 10),
|
||||
NetIntRange("armor", 0, 10),
|
||||
NetIntRange("ammocount", 0, 10),
|
||||
|
@ -138,7 +139,7 @@ Objects = [
|
|||
NetIntRange("local", 0, 1),
|
||||
NetIntRange("cid", 0, 'MAX_CLIENTS-1'),
|
||||
NetIntRange("team", -1, 1),
|
||||
|
||||
|
||||
NetIntAny("score"),
|
||||
NetIntAny("latency"),
|
||||
NetIntAny("latency_flux"),
|
||||
|
|
|
@ -632,7 +632,9 @@ int net_tcp_send(NETSOCKET sock, const void *data, int size);
|
|||
max_size - Maximum of data to write to the buffer.
|
||||
|
||||
Returns:
|
||||
Number of bytes recvived. Negative value on failure.
|
||||
Number of bytes recvived. Negative value on failure. When in
|
||||
non-blocking mode, it returns 0 when there is no more data to
|
||||
be fetched.
|
||||
*/
|
||||
int net_tcp_recv(NETSOCKET sock, void *data, int maxsize);
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ static int current_tick = 0;
|
|||
static float intratick = 0;
|
||||
static float ticktime = 0;
|
||||
|
||||
static int prev_tick = 0;
|
||||
|
||||
/* predicted time */
|
||||
static int current_predtick = 0;
|
||||
static float predintratick = 0;
|
||||
|
@ -280,6 +282,7 @@ float client_intratick() { return intratick; }
|
|||
float client_predintratick() { return predintratick; }
|
||||
float client_ticktime() { return ticktime; }
|
||||
int client_tick() { return current_tick; }
|
||||
int client_prevtick() { return current_tick; }
|
||||
int client_predtick() { return current_predtick; }
|
||||
int client_tickspeed() { return SERVER_TICK_SPEED; }
|
||||
float client_frametime() { return frametime; }
|
||||
|
@ -1193,8 +1196,9 @@ static void client_update()
|
|||
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
|
||||
snapshots[SNAP_CURRENT] = next;
|
||||
|
||||
/* set tick */
|
||||
/* set ticks */
|
||||
current_tick = snapshots[SNAP_CURRENT]->tick;
|
||||
prev_tick = snapshots[SNAP_PREV]->tick;
|
||||
|
||||
if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
|
||||
{
|
||||
|
|
|
@ -116,6 +116,11 @@ typedef struct
|
|||
*/
|
||||
int client_tick();
|
||||
|
||||
/*
|
||||
Function: client_prevtick
|
||||
Returns the tick of the previous snapshot.
|
||||
*/
|
||||
int client_prevtick();
|
||||
|
||||
/*
|
||||
Function: client_intratick
|
||||
|
|
|
@ -158,7 +158,7 @@ void PLAYERS::render_player(
|
|||
|
||||
bool stationary = player.vx < 1 && player.vx > -1;
|
||||
bool inair = col_check_point(player.x, player.y+16) == 0;
|
||||
bool want_other_dir = (player.wanted_direction == -1 && vel.x > 0) || (player.wanted_direction == 1 && vel.x < 0);
|
||||
bool want_other_dir = (player.direction == -1 && vel.x > 0) || (player.direction == 1 && vel.x < 0);
|
||||
|
||||
// evaluate animation
|
||||
float walk_time = fmod(position.x, 100.0f)/100.0f;
|
||||
|
@ -194,8 +194,8 @@ void PLAYERS::render_player(
|
|||
}
|
||||
|
||||
gameclient.effects->skidtrail(
|
||||
position+vec2(-player.wanted_direction*6,12),
|
||||
vec2(-player.wanted_direction*100*length(vel),-50)
|
||||
position+vec2(-player.direction*6,12),
|
||||
vec2(-player.direction*100*length(vel),-50)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -425,27 +425,27 @@ void PLAYERS::render_player(
|
|||
|
||||
void PLAYERS::on_render()
|
||||
{
|
||||
int num = snap_num_items(SNAP_CURRENT);
|
||||
for(int i = 0; i < num; i++)
|
||||
//int num = snap_num_items(SNAP_CURRENT);
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
// only render active characters
|
||||
if(!gameclient.snap.characters[i].active)
|
||||
continue;
|
||||
|
||||
if(item.type == NETOBJTYPE_CHARACTER)
|
||||
const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, i);
|
||||
const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, i);
|
||||
|
||||
if(prev_info && info)
|
||||
{
|
||||
const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
|
||||
const void *prev_info = snap_find_item(SNAP_PREV, NETOBJTYPE_PLAYER_INFO, item.id);
|
||||
const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, item.id);
|
||||
NETOBJ_CHARACTER prev_char = gameclient.snap.characters[i].prev;
|
||||
NETOBJ_CHARACTER cur_char = gameclient.snap.characters[i].cur;
|
||||
|
||||
if(prev && prev_info && info)
|
||||
{
|
||||
render_player(
|
||||
(const NETOBJ_CHARACTER *)prev,
|
||||
(const NETOBJ_CHARACTER *)data,
|
||||
(const NETOBJ_PLAYER_INFO *)prev_info,
|
||||
(const NETOBJ_PLAYER_INFO *)info
|
||||
);
|
||||
}
|
||||
}
|
||||
render_player(
|
||||
&prev_char,
|
||||
&cur_char,
|
||||
(const NETOBJ_PLAYER_INFO *)prev_info,
|
||||
(const NETOBJ_PLAYER_INFO *)info
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,15 +150,14 @@ void GAMECLIENT::on_console_init()
|
|||
// add the some console commands
|
||||
MACRO_REGISTER_COMMAND("team", "", con_team, this);
|
||||
MACRO_REGISTER_COMMAND("kill", "", con_kill, this);
|
||||
|
||||
|
||||
// let all the other components register their console commands
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_console_init();
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_init()
|
||||
{
|
||||
|
||||
|
||||
// init all components
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_init();
|
||||
|
@ -250,7 +249,6 @@ int GAMECLIENT::on_snapinput(int *data)
|
|||
return controls->snapinput(data);
|
||||
}
|
||||
|
||||
|
||||
void GAMECLIENT::on_connected()
|
||||
{
|
||||
layers_init();
|
||||
|
@ -479,14 +477,31 @@ void GAMECLIENT::process_events()
|
|||
}
|
||||
}
|
||||
|
||||
static void evolve(NETOBJ_CHARACTER *character, int tick)
|
||||
{
|
||||
WORLD_CORE tempworld;
|
||||
CHARACTER_CORE tempcore;
|
||||
mem_zero(&tempcore, sizeof(tempcore));
|
||||
tempcore.world = &tempworld;
|
||||
tempcore.read(character);
|
||||
//tempcore.input.direction = character->wanted_direction;
|
||||
while(character->tick < tick)
|
||||
{
|
||||
character->tick++;
|
||||
tempcore.tick(false);
|
||||
tempcore.move();
|
||||
tempcore.quantize();
|
||||
}
|
||||
|
||||
tempcore.write(character);
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_snapshot()
|
||||
{
|
||||
// clear out the invalid pointers
|
||||
mem_zero(&gameclient.snap, sizeof(gameclient.snap));
|
||||
snap.local_cid = -1;
|
||||
|
||||
static int snapshot_count = 0;
|
||||
snapshot_count++;
|
||||
|
||||
// secure snapshot
|
||||
{
|
||||
int num = snap_num_items(SNAP_CURRENT);
|
||||
|
@ -517,11 +532,10 @@ void GAMECLIENT::on_snapshot()
|
|||
}
|
||||
}
|
||||
|
||||
// setup world view
|
||||
// go trough all the items in the snapshot and gather the info we want
|
||||
{
|
||||
// 1. fetch local player
|
||||
// 2. set him to the center
|
||||
gameclient.snap.team_size[0] = gameclient.snap.team_size[1] = 0;
|
||||
snap.team_size[0] = snap.team_size[1] = 0;
|
||||
|
||||
int num = snap_num_items(SNAP_CURRENT);
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
|
@ -532,88 +546,94 @@ void GAMECLIENT::on_snapshot()
|
|||
{
|
||||
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
|
||||
|
||||
gameclient.clients[info->cid].team = info->team;
|
||||
clients[info->cid].team = info->team;
|
||||
snap.player_infos[info->cid] = info;
|
||||
|
||||
if(info->local)
|
||||
{
|
||||
gameclient.snap.local_info = info;
|
||||
const void *data = snap_find_item(SNAP_CURRENT, NETOBJTYPE_CHARACTER, item.id);
|
||||
if(data)
|
||||
{
|
||||
gameclient.snap.local_character = (const NETOBJ_CHARACTER *)data;
|
||||
gameclient.local_character_pos = vec2(gameclient.snap.local_character->x, gameclient.snap.local_character->y);
|
||||
|
||||
const void *p = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
|
||||
if(p)
|
||||
gameclient.snap.local_prev_character = (NETOBJ_CHARACTER *)p;
|
||||
}
|
||||
snap.local_cid = item.id;
|
||||
snap.local_info = info;
|
||||
|
||||
if (info->team == -1)
|
||||
gameclient.snap.spectate = true;
|
||||
snap.spectate = true;
|
||||
}
|
||||
|
||||
// calculate team-balance
|
||||
if(info->team != -1)
|
||||
gameclient.snap.team_size[info->team]++;
|
||||
snap.team_size[info->team]++;
|
||||
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_GAME)
|
||||
gameclient.snap.gameobj = (NETOBJ_GAME *)data;
|
||||
else if(item.type == NETOBJTYPE_FLAG)
|
||||
else if(item.type == NETOBJTYPE_CHARACTER)
|
||||
{
|
||||
gameclient.snap.flags[item.id%2] = (const NETOBJ_FLAG *)data;
|
||||
const void *old = snap_find_item(SNAP_PREV, NETOBJTYPE_CHARACTER, item.id);
|
||||
if(old)
|
||||
{
|
||||
snap.characters[item.id].active = true;
|
||||
snap.characters[item.id].prev = *((const NETOBJ_CHARACTER *)old);
|
||||
snap.characters[item.id].cur = *((const NETOBJ_CHARACTER *)data);
|
||||
|
||||
// perform dead reckoning
|
||||
if(snap.characters[item.id].prev.tick)
|
||||
evolve(&snap.characters[item.id].prev, client_prevtick());
|
||||
if(snap.characters[item.id].cur.tick)
|
||||
evolve(&snap.characters[item.id].cur, client_tick());
|
||||
}
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_GAME)
|
||||
snap.gameobj = (NETOBJ_GAME *)data;
|
||||
else if(item.type == NETOBJTYPE_FLAG)
|
||||
snap.flags[item.id%2] = (const NETOBJ_FLAG *)data;
|
||||
}
|
||||
}
|
||||
|
||||
// setup local pointers
|
||||
if(snap.local_cid >= 0)
|
||||
{
|
||||
SNAPSTATE::CHARACTERINFO *c = &snap.characters[snap.local_cid];
|
||||
if(c->active)
|
||||
{
|
||||
snap.local_character = &c->cur;
|
||||
snap.local_prev_character = &c->prev;
|
||||
local_character_pos = vec2(snap.local_character->x, snap.local_character->y);
|
||||
}
|
||||
}
|
||||
|
||||
// update render info
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
gameclient.clients[i].update_render_info();
|
||||
|
||||
clients[i].update_render_info();
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_predict()
|
||||
{
|
||||
// store the previous values so we can detect prediction errors
|
||||
CHARACTER_CORE before_prev_char = predicted_prev_char;
|
||||
CHARACTER_CORE before_char = predicted_char;
|
||||
|
||||
// we can't predict without our own id or own character
|
||||
if(snap.local_cid == -1 || !snap.characters[snap.local_cid].active)
|
||||
return;
|
||||
|
||||
// repredict character
|
||||
WORLD_CORE world;
|
||||
world.tuning = tuning;
|
||||
int local_cid = -1;
|
||||
|
||||
// search for players
|
||||
for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
int client_id = item.id;
|
||||
|
||||
if(item.type == NETOBJTYPE_CHARACTER)
|
||||
{
|
||||
const NETOBJ_CHARACTER *character = (const NETOBJ_CHARACTER *)data;
|
||||
gameclient.clients[client_id].predicted.world = &world;
|
||||
world.characters[client_id] = &gameclient.clients[client_id].predicted;
|
||||
|
||||
gameclient.clients[client_id].predicted.read(character);
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_PLAYER_INFO)
|
||||
{
|
||||
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
|
||||
if(info->local)
|
||||
local_cid = client_id;
|
||||
}
|
||||
if(!snap.characters[i].active)
|
||||
continue;
|
||||
|
||||
gameclient.clients[i].predicted.world = &world;
|
||||
world.characters[i] = &gameclient.clients[i].predicted;
|
||||
gameclient.clients[i].predicted.read(&snap.characters[i].cur);
|
||||
}
|
||||
|
||||
// we can't predict without our own id
|
||||
if(local_cid == -1)
|
||||
return;
|
||||
|
||||
// predict
|
||||
for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
|
||||
{
|
||||
// fetch the local
|
||||
if(tick == client_predtick() && world.characters[local_cid])
|
||||
predicted_prev_char = *world.characters[local_cid];
|
||||
if(tick == client_predtick() && world.characters[snap.local_cid])
|
||||
predicted_prev_char = *world.characters[snap.local_cid];
|
||||
|
||||
// first calculate where everyone should move
|
||||
for(int c = 0; c < MAX_CLIENTS; c++)
|
||||
|
@ -622,15 +642,17 @@ void GAMECLIENT::on_predict()
|
|||
continue;
|
||||
|
||||
mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input));
|
||||
if(local_cid == c)
|
||||
if(snap.local_cid == c)
|
||||
{
|
||||
// apply player input
|
||||
int *input = client_get_input(tick);
|
||||
if(input)
|
||||
world.characters[c]->input = *((NETOBJ_PLAYER_INPUT*)input);
|
||||
world.characters[c]->tick(true);
|
||||
}
|
||||
else
|
||||
world.characters[c]->tick(false);
|
||||
|
||||
world.characters[c]->tick();
|
||||
}
|
||||
|
||||
// move all players and quantize their data
|
||||
|
@ -643,14 +665,15 @@ void GAMECLIENT::on_predict()
|
|||
world.characters[c]->quantize();
|
||||
}
|
||||
|
||||
// check if we want to trigger effects
|
||||
if(tick > last_new_predicted_tick)
|
||||
{
|
||||
last_new_predicted_tick = tick;
|
||||
|
||||
if(local_cid != -1 && world.characters[local_cid])
|
||||
if(snap.local_cid != -1 && world.characters[snap.local_cid])
|
||||
{
|
||||
vec2 pos = world.characters[local_cid]->pos;
|
||||
int events = world.characters[local_cid]->triggered_events;
|
||||
vec2 pos = world.characters[snap.local_cid]->pos;
|
||||
int events = world.characters[snap.local_cid]->triggered_events;
|
||||
if(events&COREEVENT_GROUND_JUMP) gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
|
||||
if(events&COREEVENT_AIR_JUMP)
|
||||
{
|
||||
|
@ -662,34 +685,26 @@ void GAMECLIENT::on_predict()
|
|||
if(events&COREEVENT_HOOK_ATTACH_GROUND) gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos);
|
||||
//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
dbg_msg("predict", "%d %d %d", tick,
|
||||
(int)world.players[c]->pos.x, (int)world.players[c]->pos.y,
|
||||
(int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/
|
||||
}
|
||||
|
||||
if(tick == client_predtick() && world.characters[local_cid])
|
||||
predicted_char = *world.characters[local_cid];
|
||||
if(tick == client_predtick() && world.characters[snap.local_cid])
|
||||
predicted_char = *world.characters[snap.local_cid];
|
||||
}
|
||||
|
||||
if(config.debug && predicted_tick == client_predtick())
|
||||
if(config.debug && config.cl_predict && predicted_tick == client_predtick())
|
||||
{
|
||||
if(predicted_char.pos.x != before_char.pos.x ||
|
||||
predicted_char.pos.y != before_char.pos.y)
|
||||
{
|
||||
dbg_msg("client", "prediction error, (%d %d) (%d %d)",
|
||||
(int)before_char.pos.x, (int)before_char.pos.y,
|
||||
(int)predicted_char.pos.x, (int)predicted_char.pos.y);
|
||||
}
|
||||
NETOBJ_CHARACTER_CORE before = {0}, now = {0};
|
||||
before_char.write(&before);
|
||||
predicted_char.write(&now);
|
||||
|
||||
if(predicted_prev_char.pos.x != before_prev_char.pos.x ||
|
||||
predicted_prev_char.pos.y != before_prev_char.pos.y)
|
||||
if(mem_comp(&before, &now, sizeof(NETOBJ_CHARACTER_CORE)) != 0)
|
||||
{
|
||||
dbg_msg("client", "prediction error, prev (%d %d) (%d %d)",
|
||||
(int)before_prev_char.pos.x, (int)before_prev_char.pos.y,
|
||||
(int)predicted_prev_char.pos.x, (int)predicted_prev_char.pos.y);
|
||||
dbg_msg("client", "prediction error");
|
||||
for(unsigned i = 0; i < sizeof(NETOBJ_CHARACTER_CORE)/sizeof(int); i++)
|
||||
if(((int *)&before)[i] != ((int *)&now)[i])
|
||||
{
|
||||
dbg_msg("", "\t%d %d %d", i, ((int *)&before)[i], ((int *)&now)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -753,8 +768,6 @@ void GAMECLIENT::send_kill(int client_id)
|
|||
client_send_msg();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GAMECLIENT::con_team(void *result, void *user_data)
|
||||
{
|
||||
((GAMECLIENT*)user_data)->send_switch_team(console_arg_int(result, 0));
|
||||
|
|
|
@ -56,9 +56,26 @@ public:
|
|||
|
||||
const NETOBJ_PLAYER_INFO *player_infos[MAX_CLIENTS];
|
||||
const NETOBJ_PLAYER_INFO *info_by_score[MAX_CLIENTS];
|
||||
|
||||
int local_cid;
|
||||
int num_players;
|
||||
int team_size[2];
|
||||
bool spectate;
|
||||
|
||||
//
|
||||
struct CHARACTERINFO
|
||||
{
|
||||
bool active;
|
||||
|
||||
// snapshots
|
||||
NETOBJ_CHARACTER prev;
|
||||
NETOBJ_CHARACTER cur;
|
||||
|
||||
// interpolated position
|
||||
vec2 position;
|
||||
};
|
||||
|
||||
CHARACTERINFO characters[MAX_CLIENTS];
|
||||
};
|
||||
|
||||
SNAPSTATE snap;
|
||||
|
|
|
@ -179,18 +179,19 @@ void CHARACTER_CORE::reset()
|
|||
triggered_events = 0;
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::tick()
|
||||
void CHARACTER_CORE::tick(bool use_input)
|
||||
{
|
||||
float phys_size = 28.0f;
|
||||
triggered_events = 0;
|
||||
|
||||
// get ground state
|
||||
bool grounded = false;
|
||||
if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
|
||||
grounded = true;
|
||||
if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
|
||||
grounded = true;
|
||||
|
||||
vec2 direction = normalize(vec2(input.target_x, input.target_y));
|
||||
vec2 target_direction = normalize(vec2(input.target_x, input.target_y));
|
||||
|
||||
vel.y += world->tuning.gravity;
|
||||
|
||||
|
@ -198,13 +199,72 @@ void CHARACTER_CORE::tick()
|
|||
float accel = grounded ? world->tuning.ground_control_accel : world->tuning.air_control_accel;
|
||||
float friction = grounded ? world->tuning.ground_friction : world->tuning.air_friction;
|
||||
|
||||
// handle movement
|
||||
if(input.direction < 0)
|
||||
// handle input
|
||||
if(use_input)
|
||||
{
|
||||
direction = input.direction;
|
||||
|
||||
// setup angle
|
||||
float a = 0;
|
||||
if(input.target_x == 0)
|
||||
a = atan((float)input.target_y);
|
||||
else
|
||||
a = atan((float)input.target_y/(float)input.target_x);
|
||||
|
||||
if(input.target_x < 0)
|
||||
a = a+pi;
|
||||
|
||||
angle = (int)(a*256.0f);
|
||||
|
||||
// handle jump
|
||||
if(input.jump)
|
||||
{
|
||||
if(!(jumped&1))
|
||||
{
|
||||
if(grounded)
|
||||
{
|
||||
triggered_events |= COREEVENT_GROUND_JUMP;
|
||||
vel.y = -world->tuning.ground_jump_impulse;
|
||||
jumped |= 1;
|
||||
}
|
||||
else if(!(jumped&2))
|
||||
{
|
||||
triggered_events |= COREEVENT_AIR_JUMP;
|
||||
vel.y = -world->tuning.air_jump_impulse;
|
||||
jumped |= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
jumped &= ~1;
|
||||
|
||||
// handle hook
|
||||
if(input.hook)
|
||||
{
|
||||
if(hook_state == HOOK_IDLE)
|
||||
{
|
||||
hook_state = HOOK_FLYING;
|
||||
hook_pos = pos+target_direction*phys_size*1.5f;
|
||||
hook_dir = target_direction;
|
||||
hooked_player = -1;
|
||||
hook_tick = 0;
|
||||
triggered_events |= COREEVENT_HOOK_LAUNCH;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hooked_player = -1;
|
||||
hook_state = HOOK_IDLE;
|
||||
hook_pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
// add the speed modification according to players wanted direction
|
||||
if(direction < 0)
|
||||
vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel);
|
||||
if(input.direction > 0)
|
||||
if(direction > 0)
|
||||
vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
|
||||
|
||||
if(input.direction == 0)
|
||||
if(direction == 0)
|
||||
vel.x *= friction;
|
||||
|
||||
// handle jumping
|
||||
|
@ -213,64 +273,40 @@ void CHARACTER_CORE::tick()
|
|||
if(grounded)
|
||||
jumped &= ~2;
|
||||
|
||||
if(input.jump)
|
||||
{
|
||||
if(!(jumped&1))
|
||||
{
|
||||
if(grounded)
|
||||
{
|
||||
triggered_events |= COREEVENT_GROUND_JUMP;
|
||||
vel.y = -world->tuning.ground_jump_impulse;
|
||||
jumped |= 1;
|
||||
}
|
||||
else if(!(jumped&2))
|
||||
{
|
||||
triggered_events |= COREEVENT_AIR_JUMP;
|
||||
vel.y = -world->tuning.air_jump_impulse;
|
||||
jumped |= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
jumped &= ~1;
|
||||
|
||||
// do hook
|
||||
if(input.hook)
|
||||
if(hook_state == HOOK_IDLE)
|
||||
{
|
||||
if(hook_state == HOOK_IDLE)
|
||||
hooked_player = -1;
|
||||
hook_state = HOOK_IDLE;
|
||||
hook_pos = pos;
|
||||
}
|
||||
else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END)
|
||||
{
|
||||
hook_state++;
|
||||
}
|
||||
else if(hook_state == HOOK_RETRACT_END)
|
||||
{
|
||||
hook_state = HOOK_RETRACTED;
|
||||
triggered_events |= COREEVENT_HOOK_RETRACT;
|
||||
hook_state = HOOK_RETRACTED;
|
||||
}
|
||||
else if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed;
|
||||
if(distance(pos, new_pos) > world->tuning.hook_length)
|
||||
{
|
||||
hook_state = HOOK_FLYING;
|
||||
hook_pos = pos+direction*phys_size*1.5f;
|
||||
hook_dir = direction;
|
||||
hooked_player = -1;
|
||||
hook_tick = 0;
|
||||
triggered_events |= COREEVENT_HOOK_LAUNCH;
|
||||
hook_state = HOOK_RETRACT_START;
|
||||
new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length;
|
||||
}
|
||||
else if(hook_state >= HOOK_RETRACT_START && hook_state < HOOK_RETRACT_END)
|
||||
{
|
||||
hook_state++;
|
||||
}
|
||||
else if(hook_state == HOOK_RETRACT_END)
|
||||
{
|
||||
hook_state = HOOK_RETRACTED;
|
||||
triggered_events |= COREEVENT_HOOK_RETRACT;
|
||||
hook_state = HOOK_RETRACTED;
|
||||
}
|
||||
else if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed;
|
||||
if(distance(pos, new_pos) > world->tuning.hook_length)
|
||||
{
|
||||
hook_state = HOOK_RETRACT_START;
|
||||
new_pos = pos + normalize(new_pos-pos) * world->tuning.hook_length;
|
||||
}
|
||||
|
||||
// make sure that the hook doesn't go though the ground
|
||||
bool going_to_hit_ground = false;
|
||||
if(col_intersect_line(hook_pos, new_pos, &new_pos))
|
||||
going_to_hit_ground = true;
|
||||
|
||||
// make sure that the hook doesn't go though the ground
|
||||
bool going_to_hit_ground = false;
|
||||
if(col_intersect_line(hook_pos, new_pos, &new_pos))
|
||||
going_to_hit_ground = true;
|
||||
|
||||
// Check against other players first
|
||||
// Check against other players first
|
||||
if(world)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
CHARACTER_CORE *p = world->characters[i];
|
||||
|
@ -286,26 +322,19 @@ void CHARACTER_CORE::tick()
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
// check against ground
|
||||
if(going_to_hit_ground)
|
||||
{
|
||||
triggered_events |= COREEVENT_HOOK_ATTACH_GROUND;
|
||||
hook_state = HOOK_GRABBED;
|
||||
}
|
||||
|
||||
hook_pos = new_pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//release_hooked();
|
||||
hooked_player = -1;
|
||||
hook_state = HOOK_IDLE;
|
||||
hook_pos = pos;
|
||||
|
||||
if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
// check against ground
|
||||
if(going_to_hit_ground)
|
||||
{
|
||||
triggered_events |= COREEVENT_HOOK_ATTACH_GROUND;
|
||||
hook_state = HOOK_GRABBED;
|
||||
}
|
||||
|
||||
hook_pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_GRABBED)
|
||||
|
@ -339,7 +368,7 @@ void CHARACTER_CORE::tick()
|
|||
|
||||
// the hook will boost it's power if the player wants to move
|
||||
// in that direction. otherwise it will dampen everything abit
|
||||
if((hookvel.x < 0 && input.direction < 0) || (hookvel.x > 0 && input.direction > 0))
|
||||
if((hookvel.x < 0 && direction < 0) || (hookvel.x > 0 && direction > 0))
|
||||
hookvel.x *= 0.95f;
|
||||
else
|
||||
hookvel.x *= 0.75f;
|
||||
|
@ -362,7 +391,7 @@ void CHARACTER_CORE::tick()
|
|||
}
|
||||
}
|
||||
|
||||
if(true)
|
||||
if(world)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
|
@ -437,17 +466,8 @@ void CHARACTER_CORE::write(NETOBJ_CHARACTER_CORE *obj_core)
|
|||
obj_core->hook_dy = (int)(hook_dir.y*256.0f);
|
||||
obj_core->hooked_player = hooked_player;
|
||||
obj_core->jumped = jumped;
|
||||
|
||||
float a = 0;
|
||||
if(input.target_x == 0)
|
||||
a = atan((float)input.target_y);
|
||||
else
|
||||
a = atan((float)input.target_y/(float)input.target_x);
|
||||
|
||||
if(input.target_x < 0)
|
||||
a = a+pi;
|
||||
|
||||
obj_core->angle = (int)(a*256.0f);
|
||||
obj_core->direction = direction;
|
||||
obj_core->angle = angle;
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core)
|
||||
|
@ -464,6 +484,8 @@ void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core)
|
|||
hook_dir.y = obj_core->hook_dy/256.0f;
|
||||
hooked_player = obj_core->hooked_player;
|
||||
jumped = obj_core->jumped;
|
||||
direction = obj_core->direction;
|
||||
angle = obj_core->angle;
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::quantize()
|
||||
|
|
|
@ -137,12 +137,15 @@ public:
|
|||
int hooked_player;
|
||||
|
||||
int jumped;
|
||||
|
||||
int direction;
|
||||
int angle;
|
||||
NETOBJ_PLAYER_INPUT input;
|
||||
|
||||
int triggered_events;
|
||||
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(bool use_input);
|
||||
void move();
|
||||
|
||||
void read(const NETOBJ_CHARACTER_CORE *obj_core);
|
||||
|
|
|
@ -34,7 +34,8 @@ static INPUT_COUNT count_input(int prev, int cur)
|
|||
// player
|
||||
CHARACTER::CHARACTER()
|
||||
: ENTITY(NETOBJTYPE_CHARACTER)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
void CHARACTER::reset()
|
||||
{
|
||||
|
@ -59,6 +60,10 @@ bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team)
|
|||
core.world = &game.world.core;
|
||||
core.pos = pos;
|
||||
game.world.core.characters[player->client_id] = &core;
|
||||
|
||||
reckoning_tick = 0;
|
||||
mem_zero(&sendcore, sizeof(sendcore));
|
||||
mem_zero(&reckoningcore, sizeof(reckoningcore));
|
||||
|
||||
game.world.insert_entity(this);
|
||||
alive = true;
|
||||
|
@ -578,7 +583,7 @@ void CHARACTER::tick()
|
|||
//core.pos = pos;
|
||||
//core.jumped = jumped;
|
||||
core.input = input;
|
||||
core.tick();
|
||||
core.tick(true);
|
||||
|
||||
// handle weapons
|
||||
handle_weapons();
|
||||
|
@ -592,6 +597,16 @@ void CHARACTER::tick()
|
|||
|
||||
void CHARACTER::tick_defered()
|
||||
{
|
||||
// advance the dummy
|
||||
{
|
||||
WORLD_CORE tempworld;
|
||||
reckoningcore.world = &tempworld;
|
||||
reckoningcore.tick(false);
|
||||
reckoningcore.move();
|
||||
reckoningcore.quantize();
|
||||
}
|
||||
|
||||
//lastsentcore;
|
||||
/*if(!dead)
|
||||
{*/
|
||||
vec2 start_pos = core.pos;
|
||||
|
@ -642,6 +657,23 @@ void CHARACTER::tick_defered()
|
|||
pos.x = input.target_x;
|
||||
pos.y = input.target_y;
|
||||
}
|
||||
|
||||
// update the sendcore if needed
|
||||
{
|
||||
NETOBJ_CHARACTER predicted;
|
||||
NETOBJ_CHARACTER current;
|
||||
mem_zero(&predicted, sizeof(predicted));
|
||||
mem_zero(¤t, sizeof(current));
|
||||
reckoningcore.write(&predicted);
|
||||
core.write(¤t);
|
||||
|
||||
if(mem_comp(&predicted, ¤t, sizeof(NETOBJ_CHARACTER)) != 0)
|
||||
{
|
||||
reckoning_tick = server_tick();
|
||||
sendcore = core;
|
||||
reckoningcore = core;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CHARACTER::increase_health(int amount)
|
||||
|
@ -784,16 +816,12 @@ void CHARACTER::snap(int snaping_client)
|
|||
return;
|
||||
|
||||
NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER));
|
||||
|
||||
core.write(character);
|
||||
|
||||
// this is to make sure that players that are just standing still
|
||||
// isn't sent. this is because the physics keep bouncing between
|
||||
// 0-128 when just standing.
|
||||
// TODO: fix the physics so this isn't needed
|
||||
if(snaping_client != player->client_id && abs(character->vy) < 256.0f)
|
||||
character->vy = 0;
|
||||
|
||||
|
||||
// write down the core
|
||||
character->tick = reckoning_tick;
|
||||
sendcore.write(character);
|
||||
|
||||
// set emote
|
||||
if (emote_stop < server_tick())
|
||||
{
|
||||
emote_type = EMOTE_NORMAL;
|
||||
|
@ -809,12 +837,7 @@ void CHARACTER::snap(int snaping_client)
|
|||
character->weapon = active_weapon;
|
||||
character->attacktick = attack_tick;
|
||||
|
||||
character->wanted_direction = input.direction;
|
||||
/*
|
||||
if(input.left && !input.right)
|
||||
character->wanted_direction = -1;
|
||||
else if(!input.left && input.right)
|
||||
character->wanted_direction = 1;*/
|
||||
character->direction = input.direction;
|
||||
|
||||
if(player->client_id == snaping_client)
|
||||
{
|
||||
|
|
|
@ -79,6 +79,11 @@ public:
|
|||
|
||||
// the player core for the physics
|
||||
CHARACTER_CORE core;
|
||||
|
||||
// info for dead reckoning
|
||||
int reckoning_tick; // tick that we are performing dead reckoning from
|
||||
CHARACTER_CORE sendcore; // core that we should send
|
||||
CHARACTER_CORE reckoningcore; // the dead reckoning core
|
||||
|
||||
//
|
||||
CHARACTER();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <new>
|
||||
#include <engine/e_server_interface.h>
|
||||
#include "gamecontext.hpp"
|
||||
|
||||
|
@ -5,14 +6,28 @@ GAMECONTEXT game;
|
|||
|
||||
GAMECONTEXT::GAMECONTEXT()
|
||||
{
|
||||
clear();
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
players[i].init(-1);
|
||||
}
|
||||
|
||||
GAMECONTEXT::~GAMECONTEXT()
|
||||
{
|
||||
}
|
||||
|
||||
void GAMECONTEXT::clear()
|
||||
{
|
||||
this->~GAMECONTEXT();
|
||||
mem_zero(this, sizeof(*this));
|
||||
new (this) GAMECONTEXT();
|
||||
// reset all players
|
||||
/*
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
players[i].init(-1);
|
||||
|
||||
world.~GAMEWORLD();
|
||||
mem_zero(&world, sizeof(world));
|
||||
world.GAMEWORLD();
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ class GAMECONTEXT
|
|||
{
|
||||
public:
|
||||
GAMECONTEXT();
|
||||
~GAMECONTEXT();
|
||||
|
||||
void clear();
|
||||
|
||||
EVENTHANDLER events;
|
||||
|
|
Loading…
Reference in a new issue