Added TeeWorlds - it compiles but does not work yet (crashed my Evo yay!)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
|
||||
#include <base/math.hpp>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include "animstate.hpp"
|
||||
|
||||
static void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame)
|
||||
{
|
||||
if(seq->num_frames == 0)
|
||||
{
|
||||
frame->time = 0;
|
||||
frame->x = 0;
|
||||
frame->y = 0;
|
||||
frame->angle = 0;
|
||||
}
|
||||
else if(seq->num_frames == 1)
|
||||
{
|
||||
*frame = seq->frames[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
//time = max(0.0f, min(1.0f, time / duration)); // TODO: use clamp
|
||||
ANIM_KEYFRAME *frame1 = 0;
|
||||
ANIM_KEYFRAME *frame2 = 0;
|
||||
float blend = 0.0f;
|
||||
|
||||
// TODO: make this smarter.. binary search
|
||||
for (int i = 1; i < seq->num_frames; i++)
|
||||
{
|
||||
if (seq->frames[i-1].time <= time && seq->frames[i].time >= time)
|
||||
{
|
||||
frame1 = &seq->frames[i-1];
|
||||
frame2 = &seq->frames[i];
|
||||
blend = (time - frame1->time) / (frame2->time - frame1->time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (frame1 && frame2)
|
||||
{
|
||||
frame->time = time;
|
||||
frame->x = mix(frame1->x, frame2->x, blend);
|
||||
frame->y = mix(frame1->y, frame2->y, blend);
|
||||
frame->angle = mix(frame1->angle, frame2->angle, blend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount)
|
||||
{
|
||||
seq->x += added->x*amount;
|
||||
seq->y += added->y*amount;
|
||||
seq->angle += added->angle*amount;
|
||||
}
|
||||
|
||||
static void anim_add(ANIMSTATE *state, ANIMSTATE *added, float amount)
|
||||
{
|
||||
anim_add_keyframe(&state->body, &added->body, amount);
|
||||
anim_add_keyframe(&state->back_foot, &added->back_foot, amount);
|
||||
anim_add_keyframe(&state->front_foot, &added->front_foot, amount);
|
||||
anim_add_keyframe(&state->attach, &added->attach, amount);
|
||||
}
|
||||
|
||||
|
||||
void ANIMSTATE::set(ANIMATION *anim, float time)
|
||||
{
|
||||
anim_seq_eval(&anim->body, time, &body);
|
||||
anim_seq_eval(&anim->back_foot, time, &back_foot);
|
||||
anim_seq_eval(&anim->front_foot, time, &front_foot);
|
||||
anim_seq_eval(&anim->attach, time, &attach);
|
||||
}
|
||||
|
||||
void ANIMSTATE::add(ANIMATION *anim, float time, float amount)
|
||||
{
|
||||
ANIMSTATE add;
|
||||
add.set(anim, time);
|
||||
anim_add(this, &add, amount);
|
||||
}
|
||||
|
||||
ANIMSTATE *ANIMSTATE::get_idle()
|
||||
{
|
||||
static ANIMSTATE state;
|
||||
static bool init = true;
|
||||
|
||||
if(init)
|
||||
{
|
||||
state.set(&data->animations[ANIM_BASE], 0);
|
||||
state.add(&data->animations[ANIM_IDLE], 0, 1.0f);
|
||||
init = false;
|
||||
}
|
||||
|
||||
return &state;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef GAME_CLIENT_ANIMATION_H
|
||||
#define GAME_CLIENT_ANIMATION_H
|
||||
|
||||
class ANIMSTATE
|
||||
{
|
||||
public:
|
||||
ANIM_KEYFRAME body;
|
||||
ANIM_KEYFRAME back_foot;
|
||||
ANIM_KEYFRAME front_foot;
|
||||
ANIM_KEYFRAME attach;
|
||||
|
||||
void set(ANIMATION *anim, float time);
|
||||
void add(ANIMATION *added, float time, float amount);
|
||||
|
||||
static ANIMSTATE *get_idle();
|
||||
};
|
||||
|
||||
//void anim_seq_eval(ANIM_SEQUENCE *seq, float time, ANIM_KEYFRAME *frame);
|
||||
//void anim_eval(ANIMATION *anim, float time, ANIM_STATE *state);
|
||||
//void anim_add_keyframe(ANIM_KEYFRAME *seq, ANIM_KEYFRAME *added, float amount);
|
||||
//void anim_add(ANIM_STATE *state, ANIM_STATE *added, float amount);
|
||||
//void anim_eval_add(ANIM_STATE *state, ANIMATION *anim, float time, float amount);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <string.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/version.hpp>
|
||||
|
||||
#include "gameclient.hpp"
|
||||
#include "components/console.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
// clean hooks
|
||||
extern "C" void modc_entergame() {}
|
||||
extern "C" void modc_shutdown() {}
|
||||
extern "C" void modc_console_init() { gameclient.on_console_init(); }
|
||||
extern "C" void modc_save_config() { gameclient.on_save(); }
|
||||
extern "C" void modc_init() { gameclient.on_init(); }
|
||||
extern "C" void modc_connected() { gameclient.on_connected(); }
|
||||
extern "C" void modc_predict() { gameclient.on_predict(); }
|
||||
extern "C" void modc_newsnapshot() { gameclient.on_snapshot(); }
|
||||
extern "C" int modc_snap_input(int *data) { return gameclient.on_snapinput(data); }
|
||||
extern "C" void modc_statechange(int state, int old) { gameclient.on_statechange(state, old); }
|
||||
extern "C" void modc_render() { gameclient.on_render(); }
|
||||
extern "C" void modc_message(int msgtype) { gameclient.on_message(msgtype); }
|
||||
extern "C" void modc_rcon_line(const char *line) { gameclient.console->print_line(1, line); }
|
||||
|
||||
extern "C" const char *modc_net_version() { return GAME_NETVERSION; }
|
||||
extern "C" const char *modc_getitemname(int type) { return netobj_get_name(type); }
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef GAME_CLIENT_GAMESYSTEM_H
|
||||
#define GAME_CLIENT_GAMESYSTEM_H
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
class GAMECLIENT;
|
||||
|
||||
class COMPONENT
|
||||
{
|
||||
protected:
|
||||
GAMECLIENT *client;
|
||||
public:
|
||||
virtual ~COMPONENT() {}
|
||||
|
||||
virtual void on_statechange(int new_state, int old_state) {};
|
||||
virtual void on_console_init() {};
|
||||
virtual void on_init() {};
|
||||
virtual void on_save() {};
|
||||
virtual void on_reset() {};
|
||||
virtual void on_render() {};
|
||||
virtual void on_mapload() {};
|
||||
virtual void on_message(int msg, void *rawmsg) {}
|
||||
virtual bool on_mousemove(float x, float y) { return false; }
|
||||
virtual bool on_input(INPUT_EVENT e) { return false; }
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
#include <stdlib.h> // atoi
|
||||
#include <string.h> // strcmp
|
||||
#include <engine/e_client_interface.h>
|
||||
#include "binds.hpp"
|
||||
|
||||
bool BINDS::BINDS_SPECIAL::on_input(INPUT_EVENT e)
|
||||
{
|
||||
// don't handle invalid events and keys that arn't set to anything
|
||||
if(e.key >= KEY_F1 && e.key <= KEY_F15 && binds->keybindings[e.key][0] != 0)
|
||||
{
|
||||
int stroke = 0;
|
||||
if(e.flags&INPFLAG_PRESS)
|
||||
stroke = 1;
|
||||
console_execute_line_stroked(stroke, binds->keybindings[e.key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BINDS::BINDS()
|
||||
{
|
||||
mem_zero(keybindings, sizeof(keybindings));
|
||||
special_binds.binds = this;
|
||||
}
|
||||
|
||||
void BINDS::bind(int keyid, const char *str)
|
||||
{
|
||||
if(keyid < 0 || keyid >= KEY_LAST)
|
||||
return;
|
||||
|
||||
str_copy(keybindings[keyid], str, sizeof(keybindings[keyid]));
|
||||
if(!keybindings[keyid][0])
|
||||
dbg_msg("binds", "unbound %s (%d)", inp_key_name(keyid), keyid);
|
||||
else
|
||||
dbg_msg("binds", "bound %s (%d) = %s", inp_key_name(keyid), keyid, keybindings[keyid]);
|
||||
}
|
||||
|
||||
|
||||
bool BINDS::on_input(INPUT_EVENT e)
|
||||
{
|
||||
// don't handle invalid events and keys that arn't set to anything
|
||||
if(e.key <= 0 || e.key >= KEY_LAST || keybindings[e.key][0] == 0)
|
||||
return false;
|
||||
|
||||
int stroke = 0;
|
||||
if(e.flags&INPFLAG_PRESS)
|
||||
stroke = 1;
|
||||
console_execute_line_stroked(stroke, keybindings[e.key]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BINDS::unbindall()
|
||||
{
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
keybindings[i][0] = 0;
|
||||
}
|
||||
|
||||
const char *BINDS::get(int keyid)
|
||||
{
|
||||
if(keyid > 0 && keyid < KEY_LAST)
|
||||
return keybindings[keyid];
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *BINDS::get_key(const char *bindstr)
|
||||
{
|
||||
for(int keyid = 0; keyid < KEY_LAST; keyid++)
|
||||
{
|
||||
const char *bind = get(keyid);
|
||||
if(!bind[0])
|
||||
continue;
|
||||
|
||||
if(strcmp(bind, bindstr) == 0)
|
||||
return inp_key_name(keyid);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void BINDS::set_defaults()
|
||||
{
|
||||
// set default key bindings
|
||||
unbindall();
|
||||
bind(KEY_F1, "toggle_local_console");
|
||||
bind(KEY_F2, "toggle_remote_console");
|
||||
bind(KEY_TAB, "+scoreboard");
|
||||
bind(KEY_F10, "screenshot");
|
||||
|
||||
bind('a', "+left");
|
||||
bind('d', "+right");
|
||||
|
||||
bind(KEY_SPACE, "+jump");
|
||||
bind(KEY_MOUSE_1, "+fire");
|
||||
bind(KEY_MOUSE_2, "+hook");
|
||||
bind(KEY_LSHIFT, "+emote");
|
||||
|
||||
bind('1', "+weapon1");
|
||||
bind('2', "+weapon2");
|
||||
bind('3', "+weapon3");
|
||||
bind('4', "+weapon4");
|
||||
bind('5', "+weapon5");
|
||||
|
||||
bind(KEY_MOUSE_WHEEL_UP, "+prevweapon");
|
||||
bind(KEY_MOUSE_WHEEL_DOWN, "+nextweapon");
|
||||
|
||||
bind('t', "chat all");
|
||||
bind('y', "chat team");
|
||||
|
||||
bind(KEY_F3, "vote yes");
|
||||
bind(KEY_F4, "vote no");
|
||||
}
|
||||
|
||||
void BINDS::on_console_init()
|
||||
{
|
||||
// bindings
|
||||
MACRO_REGISTER_COMMAND("bind", "sr", CFGFLAG_CLIENT, con_bind, this, "Bind key to execute the command");
|
||||
MACRO_REGISTER_COMMAND("unbind", "s", CFGFLAG_CLIENT, con_unbind, this, "Unbind key");
|
||||
MACRO_REGISTER_COMMAND("unbindall", "", CFGFLAG_CLIENT, con_unbindall, this, "Unbind all keys");
|
||||
MACRO_REGISTER_COMMAND("dump_binds", "", CFGFLAG_CLIENT, con_dump_binds, this, "Dump binds");
|
||||
|
||||
// default bindings
|
||||
set_defaults();
|
||||
}
|
||||
|
||||
void BINDS::con_bind(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
const char *key_name = console_arg_string(result, 0);
|
||||
int id = binds->get_key_id(key_name);
|
||||
|
||||
if(!id)
|
||||
{
|
||||
dbg_msg("binds", "key %s not found", key_name);
|
||||
return;
|
||||
}
|
||||
|
||||
binds->bind(id, console_arg_string(result, 1));
|
||||
}
|
||||
|
||||
|
||||
void BINDS::con_unbind(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
const char *key_name = console_arg_string(result, 0);
|
||||
int id = binds->get_key_id(key_name);
|
||||
|
||||
if(!id)
|
||||
{
|
||||
dbg_msg("binds", "key %s not found", key_name);
|
||||
return;
|
||||
}
|
||||
|
||||
binds->bind(id, "");
|
||||
}
|
||||
|
||||
|
||||
void BINDS::con_unbindall(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
binds->unbindall();
|
||||
}
|
||||
|
||||
|
||||
void BINDS::con_dump_binds(void *result, void *user_data)
|
||||
{
|
||||
BINDS *binds = (BINDS *)user_data;
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
{
|
||||
if(binds->keybindings[i][0] == 0)
|
||||
continue;
|
||||
dbg_msg("binds", "%s (%d) = %s", inp_key_name(i), i, binds->keybindings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int BINDS::get_key_id(const char *key_name)
|
||||
{
|
||||
// check for numeric
|
||||
if(key_name[0] == '&')
|
||||
{
|
||||
int i = atoi(key_name+1);
|
||||
if(i > 0 && i < KEY_LAST)
|
||||
return i; // numeric
|
||||
}
|
||||
|
||||
// search for key
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
{
|
||||
if(strcmp(key_name, inp_key_name(i)) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void BINDS::on_save()
|
||||
{
|
||||
char buffer[256];
|
||||
char *end = buffer+sizeof(buffer)-8;
|
||||
client_save_line("unbindall");
|
||||
for(int i = 0; i < KEY_LAST; i++)
|
||||
{
|
||||
if(keybindings[i][0] == 0)
|
||||
continue;
|
||||
str_format(buffer, sizeof(buffer), "bind %s ", inp_key_name(i));
|
||||
|
||||
// process the string. we need to escape some characters
|
||||
const char *src = keybindings[i];
|
||||
char *dst = buffer + strlen(buffer);
|
||||
*dst++ = '"';
|
||||
while(*src && dst < end)
|
||||
{
|
||||
if(*src == '"' || *src == '\\') // escape \ and "
|
||||
*dst++ = '\\';
|
||||
*dst++ = *src++;
|
||||
}
|
||||
*dst++ = '"';
|
||||
*dst++ = 0;
|
||||
|
||||
client_save_line(buffer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class BINDS : public COMPONENT
|
||||
{
|
||||
char keybindings[KEY_LAST][128];
|
||||
|
||||
int get_key_id(const char *key_name);
|
||||
|
||||
static void con_bind(void *result, void *user_data);
|
||||
static void con_unbind(void *result, void *user_data);
|
||||
static void con_unbindall(void *result, void *user_data);
|
||||
static void con_dump_binds(void *result, void *user_data);
|
||||
|
||||
public:
|
||||
BINDS();
|
||||
|
||||
class BINDS_SPECIAL : public COMPONENT
|
||||
{
|
||||
public:
|
||||
BINDS *binds;
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
|
||||
BINDS_SPECIAL special_binds;
|
||||
|
||||
void bind(int keyid, const char *str);
|
||||
void set_defaults();
|
||||
void unbindall();
|
||||
const char *get(int keyid);
|
||||
const char *get_key(const char *bindstr);
|
||||
|
||||
virtual void on_save();
|
||||
virtual void on_console_init();
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
|
||||
#include "broadcast.hpp"
|
||||
|
||||
void BROADCAST::on_reset()
|
||||
{
|
||||
broadcast_time = 0;
|
||||
}
|
||||
|
||||
void BROADCAST::on_render()
|
||||
{
|
||||
gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
|
||||
|
||||
if(time_get() < broadcast_time)
|
||||
{
|
||||
float w = gfx_text_width(0, 14, broadcast_text, -1);
|
||||
gfx_text(0, 150*gfx_screenaspect()-w/2, 35, 14, broadcast_text, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void BROADCAST::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_BROADCAST)
|
||||
{
|
||||
NETMSG_SV_BROADCAST *msg = (NETMSG_SV_BROADCAST *)rawmsg;
|
||||
str_copy(broadcast_text, msg->message, sizeof(broadcast_text));
|
||||
broadcast_time = time_get()+time_freq()*10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class BROADCAST : public COMPONENT
|
||||
{
|
||||
public:
|
||||
// broadcasts
|
||||
char broadcast_text[1024];
|
||||
int64 broadcast_time;
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
extern "C" {
|
||||
#include <engine/e_config.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
}
|
||||
|
||||
#include <base/math.hpp>
|
||||
#include <game/collision.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
#include "camera.hpp"
|
||||
#include "controls.hpp"
|
||||
|
||||
CAMERA::CAMERA()
|
||||
{
|
||||
}
|
||||
|
||||
void CAMERA::on_render()
|
||||
{
|
||||
//vec2 center;
|
||||
zoom = 1.0f;
|
||||
|
||||
// update camera center
|
||||
if(gameclient.snap.spectate)
|
||||
center = gameclient.controls->mouse_pos;
|
||||
else
|
||||
{
|
||||
|
||||
float l = length(gameclient.controls->mouse_pos);
|
||||
float deadzone = config.cl_mouse_deadzone;
|
||||
float follow_factor = config.cl_mouse_followfactor/100.0f;
|
||||
vec2 camera_offset(0, 0);
|
||||
|
||||
float offset_amount = max(l-deadzone, 0.0f) * follow_factor;
|
||||
if(l > 0.0001f) // make sure that this isn't 0
|
||||
camera_offset = normalize(gameclient.controls->mouse_pos)*offset_amount;
|
||||
|
||||
center = gameclient.local_character_pos + camera_offset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class CAMERA : public COMPONENT
|
||||
{
|
||||
public:
|
||||
vec2 center;
|
||||
float zoom;
|
||||
|
||||
CAMERA();
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
#include <string.h> // strcmp
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
|
||||
#include <game/client/components/sounds.hpp>
|
||||
|
||||
#include "chat.hpp"
|
||||
|
||||
void CHAT::on_statechange(int new_state, int old_state)
|
||||
{
|
||||
if(old_state <= CLIENTSTATE_CONNECTING)
|
||||
{
|
||||
mode = MODE_NONE;
|
||||
for(int i = 0; i < MAX_LINES; i++)
|
||||
lines[i].time = 0;
|
||||
current_line = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void CHAT::con_say(void *result, void *user_data)
|
||||
{
|
||||
((CHAT*)user_data)->say(0, console_arg_string(result, 0));
|
||||
}
|
||||
|
||||
void CHAT::con_sayteam(void *result, void *user_data)
|
||||
{
|
||||
((CHAT*)user_data)->say(1, console_arg_string(result, 0));
|
||||
}
|
||||
|
||||
void CHAT::con_chat(void *result, void *user_data)
|
||||
{
|
||||
const char *mode = console_arg_string(result, 0);
|
||||
if(strcmp(mode, "all") == 0)
|
||||
((CHAT*)user_data)->enable_mode(0);
|
||||
else if(strcmp(mode, "team") == 0)
|
||||
((CHAT*)user_data)->enable_mode(1);
|
||||
else
|
||||
dbg_msg("console", "expected all or team as mode");
|
||||
}
|
||||
|
||||
void CHAT::on_console_init()
|
||||
{
|
||||
MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_CLIENT, con_say, this, "Say in chat");
|
||||
MACRO_REGISTER_COMMAND("say_team", "r", CFGFLAG_CLIENT, con_sayteam, this, "Say in team chat");
|
||||
MACRO_REGISTER_COMMAND("chat", "s", CFGFLAG_CLIENT, con_chat, this, "Enable chat with all/team mode");
|
||||
}
|
||||
|
||||
bool CHAT::on_input(INPUT_EVENT e)
|
||||
{
|
||||
if(mode == MODE_NONE)
|
||||
return false;
|
||||
|
||||
if(e.flags&INPFLAG_PRESS && e.key == KEY_ESCAPE)
|
||||
mode = MODE_NONE;
|
||||
else if(e.flags&INPFLAG_PRESS && (e.key == KEY_RETURN || e.key == KEY_KP_ENTER))
|
||||
{
|
||||
if(input.get_string()[0])
|
||||
gameclient.chat->say(mode == MODE_ALL ? 0 : 1, input.get_string());
|
||||
mode = MODE_NONE;
|
||||
}
|
||||
else
|
||||
input.process_input(e);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void CHAT::enable_mode(int team)
|
||||
{
|
||||
if(mode == MODE_NONE)
|
||||
{
|
||||
if(team)
|
||||
mode = MODE_TEAM;
|
||||
else
|
||||
mode = MODE_ALL;
|
||||
|
||||
input.clear();
|
||||
inp_clear_events();
|
||||
}
|
||||
}
|
||||
|
||||
void CHAT::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_CHAT)
|
||||
{
|
||||
NETMSG_SV_CHAT *msg = (NETMSG_SV_CHAT *)rawmsg;
|
||||
add_line(msg->cid, msg->team, msg->message);
|
||||
|
||||
if(msg->cid >= 0)
|
||||
gameclient.sounds->play(SOUNDS::CHN_GUI, SOUND_CHAT_CLIENT, 0, vec2(0,0));
|
||||
else
|
||||
gameclient.sounds->play(SOUNDS::CHN_GUI, SOUND_CHAT_SERVER, 0, vec2(0,0));
|
||||
}
|
||||
}
|
||||
|
||||
void CHAT::add_line(int client_id, int team, const char *line)
|
||||
{
|
||||
current_line = (current_line+1)%MAX_LINES;
|
||||
lines[current_line].time = time_get();
|
||||
lines[current_line].client_id = client_id;
|
||||
lines[current_line].team = team;
|
||||
lines[current_line].name_color = -2;
|
||||
|
||||
if(client_id == -1) // server message
|
||||
{
|
||||
str_copy(lines[current_line].name, "*** ", sizeof(lines[current_line].name));
|
||||
str_format(lines[current_line].text, sizeof(lines[current_line].text), "%s", line);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gameclient.clients[client_id].team == -1)
|
||||
lines[current_line].name_color = -1;
|
||||
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS)
|
||||
{
|
||||
if(gameclient.clients[client_id].team == 0)
|
||||
lines[current_line].name_color = 0;
|
||||
else if(gameclient.clients[client_id].team == 1)
|
||||
lines[current_line].name_color = 1;
|
||||
}
|
||||
|
||||
str_copy(lines[current_line].name, gameclient.clients[client_id].name, sizeof(lines[current_line].name));
|
||||
str_format(lines[current_line].text, sizeof(lines[current_line].text), ": %s", line);
|
||||
}
|
||||
|
||||
dbg_msg("chat", "%s%s", lines[current_line].name, lines[current_line].text);
|
||||
}
|
||||
|
||||
void CHAT::on_render()
|
||||
{
|
||||
gfx_mapscreen(0,0,300*gfx_screenaspect(),300);
|
||||
float x = 10.0f;
|
||||
float y = 300.0f-20.0f;
|
||||
if(mode != MODE_NONE)
|
||||
{
|
||||
// render chat input
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, x, y, 8.0f, TEXTFLAG_RENDER);
|
||||
cursor.line_width = 200.0f;
|
||||
|
||||
if(mode == MODE_ALL)
|
||||
gfx_text_ex(&cursor, "All: ", -1);
|
||||
else if(mode == MODE_TEAM)
|
||||
gfx_text_ex(&cursor, "Team: ", -1);
|
||||
else
|
||||
gfx_text_ex(&cursor, "Chat: ", -1);
|
||||
|
||||
gfx_text_ex(&cursor, input.get_string(), input.cursor_offset());
|
||||
TEXT_CURSOR marker = cursor;
|
||||
gfx_text_ex(&marker, "|", -1);
|
||||
gfx_text_ex(&cursor, input.get_string()+input.cursor_offset(), -1);
|
||||
}
|
||||
|
||||
y -= 8;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < MAX_LINES; i++)
|
||||
{
|
||||
int r = ((current_line-i)+MAX_LINES)%MAX_LINES;
|
||||
if(time_get() > lines[r].time+15*time_freq())
|
||||
break;
|
||||
|
||||
float begin = x;
|
||||
float fontsize = 7.0f;
|
||||
|
||||
// get the y offset
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, begin, 0, fontsize, 0);
|
||||
cursor.line_width = 200.0f;
|
||||
gfx_text_ex(&cursor, lines[r].name, -1);
|
||||
gfx_text_ex(&cursor, lines[r].text, -1);
|
||||
y -= cursor.y + cursor.font_size;
|
||||
|
||||
// cut off if msgs waste too much space
|
||||
if(y < 200.0f)
|
||||
break;
|
||||
|
||||
// reset the cursor
|
||||
gfx_text_set_cursor(&cursor, begin, y, fontsize, TEXTFLAG_RENDER);
|
||||
cursor.line_width = 200.0f;
|
||||
|
||||
// render name
|
||||
gfx_text_color(0.8f,0.8f,0.8f,1);
|
||||
if(lines[r].client_id == -1)
|
||||
gfx_text_color(1,1,0.5f,1); // system
|
||||
else if(lines[r].team)
|
||||
gfx_text_color(0.45f,0.9f,0.45f,1); // team message
|
||||
else if(lines[r].name_color == 0)
|
||||
gfx_text_color(1.0f,0.5f,0.5f,1); // red
|
||||
else if(lines[r].name_color == 1)
|
||||
gfx_text_color(0.7f,0.7f,1.0f,1); // blue
|
||||
else if(lines[r].name_color == -1)
|
||||
gfx_text_color(0.75f,0.5f,0.75f, 1); // spectator
|
||||
|
||||
// render name
|
||||
gfx_text_ex(&cursor, lines[r].name, -1);
|
||||
|
||||
// render line
|
||||
gfx_text_color(1,1,1,1);
|
||||
if(lines[r].client_id == -1)
|
||||
gfx_text_color(1,1,0.5f,1); // system
|
||||
else if(lines[r].team)
|
||||
gfx_text_color(0.65f,1,0.65f,1); // team message
|
||||
|
||||
gfx_text_ex(&cursor, lines[r].text, -1);
|
||||
}
|
||||
|
||||
gfx_text_color(1,1,1,1);
|
||||
}
|
||||
|
||||
void CHAT::say(int team, const char *line)
|
||||
{
|
||||
// send chat message
|
||||
NETMSG_CL_SAY msg;
|
||||
msg.team = team;
|
||||
msg.message = line;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/client/lineinput.hpp>
|
||||
|
||||
class CHAT : public COMPONENT
|
||||
{
|
||||
LINEINPUT input;
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_LINES = 10,
|
||||
};
|
||||
|
||||
struct LINE
|
||||
{
|
||||
int64 time;
|
||||
int client_id;
|
||||
int team;
|
||||
int name_color;
|
||||
char name[64];
|
||||
char text[512];
|
||||
};
|
||||
|
||||
LINE lines[MAX_LINES];
|
||||
int current_line;
|
||||
|
||||
// chat
|
||||
enum
|
||||
{
|
||||
MODE_NONE=0,
|
||||
MODE_ALL,
|
||||
MODE_TEAM,
|
||||
};
|
||||
|
||||
int mode;
|
||||
|
||||
static void con_say(void *result, void *user_data);
|
||||
static void con_sayteam(void *result, void *user_data);
|
||||
static void con_chat(void *result, void *user_data);
|
||||
|
||||
public:
|
||||
bool is_active() const { return mode != MODE_NONE; }
|
||||
|
||||
void add_line(int client_id, int team, const char *line);
|
||||
|
||||
void enable_mode(int team);
|
||||
|
||||
void say(int team, const char *line);
|
||||
|
||||
virtual void on_console_init();
|
||||
virtual void on_statechange(int new_state, int old_state);
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
@@ -0,0 +1,538 @@
|
||||
//#include "gc_console.hpp"
|
||||
#include <math.h>
|
||||
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <base/system.h>
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
extern "C" {
|
||||
#include <engine/e_ringbuffer.h>
|
||||
#include <engine/client/ec_font.h>
|
||||
}
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <game/client/ui.hpp>
|
||||
|
||||
#include <game/version.hpp>
|
||||
|
||||
#include <game/client/lineinput.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
#include "console.hpp"
|
||||
|
||||
enum
|
||||
{
|
||||
CONSOLE_CLOSED,
|
||||
CONSOLE_OPENING,
|
||||
CONSOLE_OPEN,
|
||||
CONSOLE_CLOSING,
|
||||
};
|
||||
|
||||
CONSOLE::INSTANCE::INSTANCE(int t)
|
||||
{
|
||||
// init ringbuffers
|
||||
history = ringbuf_init(history_data, sizeof(history_data), RINGBUF_FLAG_RECYCLE);
|
||||
backlog = ringbuf_init(backlog_data, sizeof(backlog_data), RINGBUF_FLAG_RECYCLE);
|
||||
|
||||
history_entry = 0x0;
|
||||
|
||||
type = t;
|
||||
|
||||
if(t == 0)
|
||||
completion_flagmask = CFGFLAG_CLIENT;
|
||||
else
|
||||
completion_flagmask = CFGFLAG_SERVER;
|
||||
|
||||
completion_buffer[0] = 0;
|
||||
completion_chosen = -1;
|
||||
|
||||
command = 0x0;
|
||||
}
|
||||
|
||||
void CONSOLE::INSTANCE::execute_line(const char *line)
|
||||
{
|
||||
if(type == 0)
|
||||
console_execute_line(line);
|
||||
else
|
||||
{
|
||||
if(client_rcon_authed())
|
||||
client_rcon(line);
|
||||
else
|
||||
client_rcon_auth("", line);
|
||||
}
|
||||
}
|
||||
|
||||
void CONSOLE::INSTANCE::possible_commands_complete_callback(const char *str, void *user)
|
||||
{
|
||||
CONSOLE::INSTANCE *instance = (CONSOLE::INSTANCE *)user;
|
||||
if(instance->completion_chosen == instance->completion_enumeration_count)
|
||||
instance->input.set(str);
|
||||
instance->completion_enumeration_count++;
|
||||
}
|
||||
|
||||
void CONSOLE::INSTANCE::on_input(INPUT_EVENT e)
|
||||
{
|
||||
bool handled = false;
|
||||
|
||||
if(e.flags&INPFLAG_PRESS)
|
||||
{
|
||||
if(e.key == KEY_RETURN || e.key == KEY_KP_ENTER)
|
||||
{
|
||||
if(input.get_string()[0])
|
||||
{
|
||||
char *entry = (char *)ringbuf_allocate(history, input.get_length()+1);
|
||||
mem_copy(entry, input.get_string(), input.get_length()+1);
|
||||
|
||||
execute_line(input.get_string());
|
||||
input.clear();
|
||||
history_entry = 0x0;
|
||||
}
|
||||
|
||||
handled = true;
|
||||
}
|
||||
else if (e.key == KEY_UP)
|
||||
{
|
||||
if (history_entry)
|
||||
{
|
||||
char *test = (char *)ringbuf_prev(history, history_entry);
|
||||
|
||||
if (test)
|
||||
history_entry = test;
|
||||
}
|
||||
else
|
||||
history_entry = (char *)ringbuf_last(history);
|
||||
|
||||
if (history_entry)
|
||||
{
|
||||
unsigned int len = strlen(history_entry);
|
||||
if (len < sizeof(input) - 1)
|
||||
input.set(history_entry);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if (e.key == KEY_DOWN)
|
||||
{
|
||||
if (history_entry)
|
||||
history_entry = (char *)ringbuf_next(history, history_entry);
|
||||
|
||||
if (history_entry)
|
||||
{
|
||||
unsigned int len = strlen(history_entry);
|
||||
if (len < sizeof(input) - 1)
|
||||
input.set(history_entry);
|
||||
}
|
||||
else
|
||||
input.clear();
|
||||
handled = true;
|
||||
}
|
||||
else if(e.key == KEY_TAB)
|
||||
{
|
||||
completion_chosen++;
|
||||
completion_enumeration_count = 0;
|
||||
console_possible_commands(completion_buffer, completion_flagmask, possible_commands_complete_callback, this);
|
||||
|
||||
// handle wrapping
|
||||
if(completion_enumeration_count && completion_chosen >= completion_enumeration_count)
|
||||
{
|
||||
completion_chosen %= completion_enumeration_count;
|
||||
completion_enumeration_count = 0;
|
||||
console_possible_commands(completion_buffer, completion_flagmask, possible_commands_complete_callback, this);
|
||||
}
|
||||
}
|
||||
|
||||
if(e.key != KEY_TAB)
|
||||
{
|
||||
completion_chosen = -1;
|
||||
str_copy(completion_buffer, input.get_string(), sizeof(completion_buffer));
|
||||
}
|
||||
|
||||
// find the current command
|
||||
{
|
||||
char buf[64] = {0};
|
||||
const char *src = get_string();
|
||||
int i = 0;
|
||||
for(; i < (int)sizeof(buf) && *src && *src != ' ' && *src != ' '; i++, src++)
|
||||
buf[i] = *src;
|
||||
buf[i] = 0;
|
||||
|
||||
command = console_get_command(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if(!handled)
|
||||
input.process_input(e);
|
||||
}
|
||||
|
||||
void CONSOLE::INSTANCE::print_line(const char *line)
|
||||
{
|
||||
int len = strlen(line);
|
||||
|
||||
if (len > 255)
|
||||
len = 255;
|
||||
|
||||
char *entry = (char *)ringbuf_allocate(backlog, len+1);
|
||||
mem_copy(entry, line, len+1);
|
||||
}
|
||||
|
||||
CONSOLE::CONSOLE()
|
||||
: local_console(0), remote_console(1)
|
||||
{
|
||||
console_type = 0;
|
||||
console_state = CONSOLE_CLOSED;
|
||||
state_change_end = 0.0f;
|
||||
state_change_duration = 0.1f;
|
||||
}
|
||||
|
||||
float CONSOLE::time_now()
|
||||
{
|
||||
static long long time_start = time_get();
|
||||
return float(time_get()-time_start)/float(time_freq());
|
||||
}
|
||||
|
||||
CONSOLE::INSTANCE *CONSOLE::current_console()
|
||||
{
|
||||
if(console_type != 0)
|
||||
return &remote_console;
|
||||
return &local_console;
|
||||
}
|
||||
|
||||
void CONSOLE::on_reset()
|
||||
{
|
||||
}
|
||||
|
||||
// only defined for 0<=t<=1
|
||||
static float console_scale_func(float t)
|
||||
{
|
||||
//return t;
|
||||
return sinf(acosf(1.0f-t));
|
||||
}
|
||||
|
||||
struct RENDERINFO
|
||||
{
|
||||
TEXT_CURSOR cursor;
|
||||
const char *current_cmd;
|
||||
int wanted_completion;
|
||||
int enum_count;
|
||||
};
|
||||
|
||||
void CONSOLE::possible_commands_render_callback(const char *str, void *user)
|
||||
{
|
||||
RENDERINFO *info = (RENDERINFO *)user;
|
||||
|
||||
if(info->enum_count == info->wanted_completion)
|
||||
{
|
||||
float tw = gfx_text_width(info->cursor.font_set, info->cursor.font_size, str, -1);
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,0.85f);
|
||||
draw_round_rect(info->cursor.x-3, info->cursor.y, tw+5, info->cursor.font_size+4, info->cursor.font_size/3);
|
||||
gfx_quads_end();
|
||||
|
||||
gfx_text_color(0.05f, 0.05f, 0.05f,1);
|
||||
gfx_text_ex(&info->cursor, str, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *match_start = str_find_nocase(str, info->current_cmd);
|
||||
|
||||
if(match_start)
|
||||
{
|
||||
gfx_text_color(0.5f,0.5f,0.5f,1);
|
||||
gfx_text_ex(&info->cursor, str, match_start-str);
|
||||
gfx_text_color(229.0f/255.0f,185.0f/255.0f,4.0f/255.0f,1);
|
||||
gfx_text_ex(&info->cursor, match_start, strlen(info->current_cmd));
|
||||
gfx_text_color(0.5f,0.5f,0.5f,1);
|
||||
gfx_text_ex(&info->cursor, match_start+strlen(info->current_cmd), -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_text_color(0.75f,0.75f,0.75f,1);
|
||||
gfx_text_ex(&info->cursor, str, -1);
|
||||
}
|
||||
}
|
||||
|
||||
info->enum_count++;
|
||||
info->cursor.x += 7.0f;
|
||||
}
|
||||
|
||||
void CONSOLE::on_render()
|
||||
{
|
||||
RECT screen = *ui_screen();
|
||||
float console_max_height = screen.h*3/5.0f;
|
||||
float console_height;
|
||||
|
||||
float progress = (time_now()-(state_change_end-state_change_duration))/float(state_change_duration);
|
||||
|
||||
if (progress >= 1.0f)
|
||||
{
|
||||
if (console_state == CONSOLE_CLOSING)
|
||||
console_state = CONSOLE_CLOSED;
|
||||
else if (console_state == CONSOLE_OPENING)
|
||||
console_state = CONSOLE_OPEN;
|
||||
|
||||
progress = 1.0f;
|
||||
}
|
||||
|
||||
if (console_state == CONSOLE_OPEN && config.cl_editor)
|
||||
toggle(0);
|
||||
|
||||
if (console_state == CONSOLE_CLOSED)
|
||||
return;
|
||||
|
||||
if (console_state == CONSOLE_OPEN)
|
||||
inp_mouse_mode_absolute();
|
||||
|
||||
float console_height_scale;
|
||||
|
||||
if (console_state == CONSOLE_OPENING)
|
||||
console_height_scale = console_scale_func(progress);
|
||||
else if (console_state == CONSOLE_CLOSING)
|
||||
console_height_scale = console_scale_func(1.0f-progress);
|
||||
else //if (console_state == CONSOLE_OPEN)
|
||||
console_height_scale = console_scale_func(1.0f);
|
||||
|
||||
console_height = console_height_scale*console_max_height;
|
||||
|
||||
gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);
|
||||
|
||||
// do console shadow
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolorvertex(0, 0,0,0, 0.5f);
|
||||
gfx_setcolorvertex(1, 0,0,0, 0.5f);
|
||||
gfx_setcolorvertex(2, 0,0,0, 0.0f);
|
||||
gfx_setcolorvertex(3, 0,0,0, 0.0f);
|
||||
gfx_quads_drawTL(0,console_height,screen.w,10.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
// do background
|
||||
gfx_texture_set(data->images[IMAGE_CONSOLE_BG].id);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0.2f, 0.2f, 0.2f,0.9f);
|
||||
if(console_type != 0)
|
||||
gfx_setcolor(0.4f, 0.2f, 0.2f,0.9f);
|
||||
gfx_quads_setsubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0);
|
||||
gfx_quads_drawTL(0,0,screen.w,console_height);
|
||||
gfx_quads_end();
|
||||
|
||||
// do small bar shadow
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolorvertex(0, 0,0,0, 0.0f);
|
||||
gfx_setcolorvertex(1, 0,0,0, 0.0f);
|
||||
gfx_setcolorvertex(2, 0,0,0, 0.25f);
|
||||
gfx_setcolorvertex(3, 0,0,0, 0.25f);
|
||||
gfx_quads_drawTL(0,console_height-20,screen.w,10);
|
||||
gfx_quads_end();
|
||||
|
||||
// do the lower bar
|
||||
gfx_texture_set(data->images[IMAGE_CONSOLE_BAR].id);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(1.0f, 1.0f, 1.0f, 0.9f);
|
||||
gfx_quads_setsubset(0,0.1f,screen.w*0.015f,1-0.1f);
|
||||
gfx_quads_drawTL(0,console_height-10.0f,screen.w,10.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
console_height -= 22.0f;
|
||||
|
||||
INSTANCE *console = current_console();
|
||||
|
||||
{
|
||||
float font_size = 10.0f;
|
||||
float row_height = font_size*1.25f;
|
||||
float x = 3;
|
||||
float y = console_height - row_height - 2;
|
||||
|
||||
// render prompt
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, x, y, font_size, TEXTFLAG_RENDER);
|
||||
|
||||
RENDERINFO info;
|
||||
info.wanted_completion = console->completion_chosen;
|
||||
info.enum_count = 0;
|
||||
info.current_cmd = console->completion_buffer;
|
||||
gfx_text_set_cursor(&info.cursor, x, y+12.0f, font_size, TEXTFLAG_RENDER);
|
||||
|
||||
const char *prompt = "> ";
|
||||
if(console_type)
|
||||
{
|
||||
if(client_state() == CLIENTSTATE_ONLINE)
|
||||
{
|
||||
if(client_rcon_authed())
|
||||
prompt = "rcon> ";
|
||||
else
|
||||
prompt = "ENTER PASSWORD> ";
|
||||
}
|
||||
else
|
||||
prompt = "NOT CONNECTED> ";
|
||||
}
|
||||
|
||||
gfx_text_ex(&cursor, prompt, -1);
|
||||
|
||||
// render console input
|
||||
gfx_text_ex(&cursor, console->input.get_string(), console->input.cursor_offset());
|
||||
TEXT_CURSOR marker = cursor;
|
||||
gfx_text_ex(&marker, "|", -1);
|
||||
gfx_text_ex(&cursor, console->input.get_string()+console->input.cursor_offset(), -1);
|
||||
|
||||
// render version
|
||||
char buf[128];
|
||||
str_format(buf, sizeof(buf), "v%s", GAME_VERSION);
|
||||
float version_width = gfx_text_width(0, font_size, buf, -1);
|
||||
gfx_text(0, screen.w-version_width-5, y, font_size, buf, -1);
|
||||
|
||||
// render possible commands
|
||||
if(console->input.get_string()[0] != 0)
|
||||
{
|
||||
console_possible_commands(console->completion_buffer, console->completion_flagmask, possible_commands_render_callback, &info);
|
||||
|
||||
if(info.enum_count <= 0)
|
||||
{
|
||||
if(console->command)
|
||||
{
|
||||
|
||||
char buf[512];
|
||||
str_format(buf, sizeof(buf), "Help: %s ", console->command->help);
|
||||
gfx_text_ex(&info.cursor, buf, -1);
|
||||
gfx_text_color(0.75f, 0.75f, 0.75f, 1);
|
||||
str_format(buf, sizeof(buf), "Syntax: %s %s", console->command->name, console->command->params);
|
||||
gfx_text_ex(&info.cursor, buf, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
gfx_text_color(1,1,1,1);
|
||||
|
||||
// render log
|
||||
y -= row_height;
|
||||
char *entry = (char *)ringbuf_last(console->backlog);
|
||||
while (y > 0.0f && entry)
|
||||
{
|
||||
gfx_text(0, x, y, font_size, entry, -1);
|
||||
y -= row_height;
|
||||
|
||||
entry = (char *)ringbuf_prev(console->backlog, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CONSOLE::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
}
|
||||
|
||||
bool CONSOLE::on_input(INPUT_EVENT e)
|
||||
{
|
||||
if(console_state == CONSOLE_CLOSED)
|
||||
return false;
|
||||
if(e.key >= KEY_F1 && e.key <= KEY_F15)
|
||||
return false;
|
||||
|
||||
if(e.key == KEY_ESCAPE && (e.flags&INPFLAG_PRESS))
|
||||
toggle(console_type);
|
||||
else
|
||||
current_console()->on_input(e);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CONSOLE::toggle(int type)
|
||||
{
|
||||
if(console_type != type && (console_state == CONSOLE_OPEN || console_state == CONSOLE_OPENING))
|
||||
{
|
||||
// don't toggle console, just switch what console to use
|
||||
}
|
||||
else
|
||||
{
|
||||
if (console_state == CONSOLE_CLOSED || console_state == CONSOLE_OPEN)
|
||||
{
|
||||
state_change_end = time_now()+state_change_duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
float progress = state_change_end-time_now();
|
||||
float reversed_progress = state_change_duration-progress;
|
||||
|
||||
state_change_end = time_now()+reversed_progress;
|
||||
}
|
||||
|
||||
if (console_state == CONSOLE_CLOSED || console_state == CONSOLE_CLOSING)
|
||||
{
|
||||
inp_mouse_mode_absolute();
|
||||
console_state = CONSOLE_OPENING;
|
||||
}
|
||||
else
|
||||
{
|
||||
inp_mouse_mode_relative();
|
||||
console_state = CONSOLE_CLOSING;
|
||||
}
|
||||
}
|
||||
|
||||
console_type = type;
|
||||
}
|
||||
|
||||
void CONSOLE::con_toggle_local_console(void *result, void *user_data)
|
||||
{
|
||||
((CONSOLE *)user_data)->toggle(0);
|
||||
}
|
||||
|
||||
void CONSOLE::con_toggle_remote_console(void *result, void *user_data)
|
||||
{
|
||||
((CONSOLE *)user_data)->toggle(1);
|
||||
}
|
||||
|
||||
void CONSOLE::client_console_print_callback(const char *str, void *user_data)
|
||||
{
|
||||
((CONSOLE *)user_data)->local_console.print_line(str);
|
||||
}
|
||||
|
||||
void CONSOLE::print_line(int type, const char *line)
|
||||
{
|
||||
if(type == 0)
|
||||
local_console.print_line(line);
|
||||
else if(type == 1)
|
||||
remote_console.print_line(line);
|
||||
}
|
||||
|
||||
void CONSOLE::on_console_init()
|
||||
{
|
||||
//
|
||||
console_register_print_callback(client_console_print_callback, this);
|
||||
|
||||
MACRO_REGISTER_COMMAND("toggle_local_console", "", CFGFLAG_CLIENT, con_toggle_local_console, this, "Toggle local console");
|
||||
MACRO_REGISTER_COMMAND("toggle_remote_console", "", CFGFLAG_CLIENT, con_toggle_remote_console, this, "Toggle remote console");
|
||||
}
|
||||
|
||||
/*
|
||||
static void con_team(void *result, void *user_data)
|
||||
{
|
||||
send_switch_team(console_arg_int(result, 0));
|
||||
}
|
||||
|
||||
static void con_kill(void *result, void *user_data)
|
||||
{
|
||||
send_kill(-1);
|
||||
}
|
||||
|
||||
void send_kill(int client_id);
|
||||
|
||||
static void con_emote(void *result, void *user_data)
|
||||
{
|
||||
send_emoticon(console_arg_int(result, 0));
|
||||
}
|
||||
|
||||
extern void con_chat(void *result, void *user_data);
|
||||
|
||||
void client_console_init()
|
||||
{
|
||||
//
|
||||
MACRO_REGISTER_COMMAND("team", "i", con_team, 0x0);
|
||||
MACRO_REGISTER_COMMAND("kill", "", con_kill, 0x0);
|
||||
|
||||
// chatting
|
||||
MACRO_REGISTER_COMMAND("emote", "i", con_emote, 0);
|
||||
MACRO_REGISTER_COMMAND("+emote", "", con_key_input_state, &emoticon_selector_active);
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,70 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/client/lineinput.hpp>
|
||||
|
||||
class CONSOLE : public COMPONENT
|
||||
{
|
||||
class INSTANCE
|
||||
{
|
||||
public:
|
||||
char history_data[65536];
|
||||
struct RINGBUFFER *history;
|
||||
char *history_entry;
|
||||
|
||||
char backlog_data[65536];
|
||||
struct RINGBUFFER *backlog;
|
||||
|
||||
LINEINPUT input;
|
||||
|
||||
int type;
|
||||
int completion_enumeration_count;
|
||||
|
||||
public:
|
||||
char completion_buffer[128];
|
||||
int completion_chosen;
|
||||
int completion_flagmask;
|
||||
|
||||
COMMAND *command;
|
||||
|
||||
INSTANCE(int t);
|
||||
|
||||
void execute_line(const char *line);
|
||||
|
||||
void on_input(INPUT_EVENT e);
|
||||
void print_line(const char *line);
|
||||
|
||||
const char *get_string() const { return input.get_string(); }
|
||||
|
||||
static void possible_commands_complete_callback(const char *str, void *user);
|
||||
};
|
||||
|
||||
INSTANCE local_console;
|
||||
INSTANCE remote_console;
|
||||
|
||||
INSTANCE *current_console();
|
||||
float time_now();
|
||||
|
||||
int console_type;
|
||||
int console_state;
|
||||
float state_change_end;
|
||||
float state_change_duration;
|
||||
|
||||
|
||||
void toggle(int type);
|
||||
|
||||
static void possible_commands_render_callback(const char *str, void *user);
|
||||
static void client_console_print_callback(const char *str, void *user_data);
|
||||
static void con_toggle_local_console(void *result, void *user_data);
|
||||
static void con_toggle_remote_console(void *result, void *user_data);
|
||||
|
||||
public:
|
||||
CONSOLE();
|
||||
|
||||
void print_line(int type, const char *line);
|
||||
|
||||
virtual void on_console_init();
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <base/math.hpp>
|
||||
#include <game/collision.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/client/components/chat.hpp>
|
||||
#include <game/client/components/menus.hpp>
|
||||
|
||||
#include "controls.hpp"
|
||||
|
||||
CONTROLS::CONTROLS()
|
||||
{
|
||||
}
|
||||
|
||||
static void con_key_input_state(void *result, void *user_data)
|
||||
{
|
||||
((int *)user_data)[0] = console_arg_int(result, 0);
|
||||
}
|
||||
|
||||
static void con_key_input_counter(void *result, void *user_data)
|
||||
{
|
||||
int *v = (int *)user_data;
|
||||
if(((*v)&1) != console_arg_int(result, 0))
|
||||
(*v)++;
|
||||
*v &= INPUT_STATE_MASK;
|
||||
}
|
||||
|
||||
struct INPUTSET
|
||||
{
|
||||
CONTROLS *controls;
|
||||
int *variable;
|
||||
int value;
|
||||
};
|
||||
|
||||
static void con_key_input_set(void *result, void *user_data)
|
||||
{
|
||||
INPUTSET *set = (INPUTSET *)user_data;
|
||||
*set->variable = console_arg_int(result, 0) ? *set->variable = set->value : 0;
|
||||
}
|
||||
|
||||
static void con_key_input_nextprev_weapon(void *result, void *user_data)
|
||||
{
|
||||
INPUTSET *set = (INPUTSET *)user_data;
|
||||
con_key_input_counter(result, set->variable);
|
||||
set->controls->input_data.wanted_weapon = 0;
|
||||
}
|
||||
|
||||
void CONTROLS::on_console_init()
|
||||
{
|
||||
// game commands
|
||||
MACRO_REGISTER_COMMAND("+left", "", CFGFLAG_CLIENT, con_key_input_state, &input_direction_left, "Move left");
|
||||
MACRO_REGISTER_COMMAND("+right", "", CFGFLAG_CLIENT, con_key_input_state, &input_direction_right, "Move right");
|
||||
MACRO_REGISTER_COMMAND("+jump", "", CFGFLAG_CLIENT, con_key_input_state, &input_data.jump, "Jump");
|
||||
MACRO_REGISTER_COMMAND("+hook", "", CFGFLAG_CLIENT, con_key_input_state, &input_data.hook, "Hook");
|
||||
MACRO_REGISTER_COMMAND("+fire", "", CFGFLAG_CLIENT, con_key_input_counter, &input_data.fire, "Fire");
|
||||
|
||||
{ static INPUTSET set = {this, &input_data.wanted_weapon, 1}; MACRO_REGISTER_COMMAND("+weapon1", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to hammer"); }
|
||||
{ static INPUTSET set = {this, &input_data.wanted_weapon, 2}; MACRO_REGISTER_COMMAND("+weapon2", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to gun"); }
|
||||
{ static INPUTSET set = {this, &input_data.wanted_weapon, 3}; MACRO_REGISTER_COMMAND("+weapon3", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to shotgun"); }
|
||||
{ static INPUTSET set = {this, &input_data.wanted_weapon, 4}; MACRO_REGISTER_COMMAND("+weapon4", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to grenade"); }
|
||||
{ static INPUTSET set = {this, &input_data.wanted_weapon, 5}; MACRO_REGISTER_COMMAND("+weapon5", "", CFGFLAG_CLIENT, con_key_input_set, (void *)&set, "Switch to rifle"); }
|
||||
|
||||
{ static INPUTSET set = {this, &input_data.next_weapon, 0}; MACRO_REGISTER_COMMAND("+nextweapon", "", CFGFLAG_CLIENT, con_key_input_nextprev_weapon, (void *)&set, "Switch to next weapon"); }
|
||||
{ static INPUTSET set = {this, &input_data.prev_weapon, 0}; MACRO_REGISTER_COMMAND("+prevweapon", "", CFGFLAG_CLIENT, con_key_input_nextprev_weapon, (void *)&set, "Switch to previous weapon"); }
|
||||
}
|
||||
|
||||
void CONTROLS::on_message(int msg, void *rawmsg)
|
||||
{
|
||||
if(msg == NETMSGTYPE_SV_WEAPONPICKUP)
|
||||
{
|
||||
NETMSG_SV_WEAPONPICKUP *msg = (NETMSG_SV_WEAPONPICKUP *)rawmsg;
|
||||
if(config.cl_autoswitch_weapons)
|
||||
input_data.wanted_weapon = msg->weapon+1;
|
||||
}
|
||||
}
|
||||
|
||||
int CONTROLS::snapinput(int *data)
|
||||
{
|
||||
static NETOBJ_PLAYER_INPUT last_data = {0};
|
||||
static int64 last_send_time = 0;
|
||||
bool send = false;
|
||||
|
||||
// update player state
|
||||
if(gameclient.chat->is_active())
|
||||
input_data.player_state = PLAYERSTATE_CHATTING;
|
||||
else if(gameclient.menus->is_active())
|
||||
input_data.player_state = PLAYERSTATE_IN_MENU;
|
||||
else
|
||||
input_data.player_state = PLAYERSTATE_PLAYING;
|
||||
|
||||
if(last_data.player_state != input_data.player_state)
|
||||
send = true;
|
||||
|
||||
last_data.player_state = input_data.player_state;
|
||||
|
||||
// we freeze the input if chat or menu is activated
|
||||
if(input_data.player_state != PLAYERSTATE_PLAYING)
|
||||
{
|
||||
last_data.direction = 0;
|
||||
last_data.hook = 0;
|
||||
last_data.jump = 0;
|
||||
input_data = last_data;
|
||||
|
||||
input_direction_left = 0;
|
||||
input_direction_right = 0;
|
||||
|
||||
mem_copy(data, &input_data, sizeof(input_data));
|
||||
|
||||
// send once a second just to be sure
|
||||
if(time_get() > last_send_time + time_freq())
|
||||
send = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
input_data.target_x = (int)mouse_pos.x;
|
||||
input_data.target_y = (int)mouse_pos.y;
|
||||
if(!input_data.target_x && !input_data.target_y)
|
||||
input_data.target_y = 1;
|
||||
|
||||
// set direction
|
||||
input_data.direction = 0;
|
||||
if(input_direction_left && !input_direction_right)
|
||||
input_data.direction = -1;
|
||||
if(!input_direction_left && input_direction_right)
|
||||
input_data.direction = 1;
|
||||
|
||||
// stress testing
|
||||
if(config.dbg_stress)
|
||||
{
|
||||
float t = client_localtime();
|
||||
mem_zero(&input_data, sizeof(input_data));
|
||||
|
||||
input_data.direction = ((int)t/2)&1;
|
||||
input_data.jump = ((int)t);
|
||||
input_data.fire = ((int)(t*10));
|
||||
input_data.hook = ((int)(t*2))&1;
|
||||
input_data.wanted_weapon = ((int)t)%NUM_WEAPONS;
|
||||
input_data.target_x = (int)(sinf(t*3)*100.0f);
|
||||
input_data.target_y = (int)(cosf(t*3)*100.0f);
|
||||
}
|
||||
|
||||
// check if we need to send input
|
||||
if(input_data.direction != last_data.direction) send = true;
|
||||
else if(input_data.jump != last_data.jump) send = true;
|
||||
else if(input_data.fire != last_data.fire) send = true;
|
||||
else if(input_data.hook != last_data.hook) send = true;
|
||||
else if(input_data.player_state != last_data.player_state) send = true;
|
||||
else if(input_data.wanted_weapon != last_data.wanted_weapon) send = true;
|
||||
else if(input_data.next_weapon != last_data.next_weapon) send = true;
|
||||
else if(input_data.prev_weapon != last_data.prev_weapon) send = true;
|
||||
|
||||
// send at at least 10hz
|
||||
if(time_get() > last_send_time + time_freq()/25)
|
||||
send = true;
|
||||
}
|
||||
|
||||
// copy and return size
|
||||
last_data = input_data;
|
||||
|
||||
if(!send)
|
||||
return 0;
|
||||
|
||||
last_send_time = time_get();
|
||||
mem_copy(data, &input_data, sizeof(input_data));
|
||||
return sizeof(input_data);
|
||||
}
|
||||
|
||||
void CONTROLS::on_render()
|
||||
{
|
||||
// update target pos
|
||||
if(!((gameclient.snap.gameobj && gameclient.snap.gameobj->paused) || gameclient.snap.spectate))
|
||||
target_pos = gameclient.local_character_pos + mouse_pos;
|
||||
}
|
||||
|
||||
bool CONTROLS::on_mousemove(float x, float y)
|
||||
{
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->paused)
|
||||
return false;
|
||||
mouse_pos += vec2(x, y); // TODO: ugly
|
||||
|
||||
//
|
||||
float camera_max_distance = 200.0f;
|
||||
float follow_factor = config.cl_mouse_followfactor/100.0f;
|
||||
float deadzone = config.cl_mouse_deadzone;
|
||||
float mouse_max = min(camera_max_distance/follow_factor + deadzone, (float)config.cl_mouse_max_distance);
|
||||
|
||||
//vec2 camera_offset(0, 0);
|
||||
|
||||
if(gameclient.snap.spectate)
|
||||
{
|
||||
if(mouse_pos.x < 200.0f) mouse_pos.x = 200.0f;
|
||||
if(mouse_pos.y < 200.0f) mouse_pos.y = 200.0f;
|
||||
if(mouse_pos.x > col_width()*32-200.0f) mouse_pos.x = col_width()*32-200.0f;
|
||||
if(mouse_pos.y > col_height()*32-200.0f) mouse_pos.y = col_height()*32-200.0f;
|
||||
|
||||
target_pos = mouse_pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
float l = length(mouse_pos);
|
||||
|
||||
if(l > mouse_max)
|
||||
{
|
||||
mouse_pos = normalize(mouse_pos)*mouse_max;
|
||||
l = mouse_max;
|
||||
}
|
||||
|
||||
//float offset_amount = max(l-deadzone, 0.0f) * follow_factor;
|
||||
//if(l > 0.0001f) // make sure that this isn't 0
|
||||
//camera_offset = normalize(mouse_pos)*offset_amount;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class CONTROLS : public COMPONENT
|
||||
{
|
||||
public:
|
||||
vec2 mouse_pos;
|
||||
vec2 target_pos;
|
||||
|
||||
NETOBJ_PLAYER_INPUT input_data;
|
||||
int input_direction_left;
|
||||
int input_direction_right;
|
||||
|
||||
CONTROLS();
|
||||
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msg, void *rawmsg);
|
||||
virtual bool on_mousemove(float x, float y);
|
||||
virtual void on_console_init();
|
||||
|
||||
int snapinput(int *data);
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/gamecore.hpp> // get_angle
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include "damageind.hpp"
|
||||
|
||||
DAMAGEIND::DAMAGEIND()
|
||||
{
|
||||
lastupdate = 0;
|
||||
num_items = 0;
|
||||
}
|
||||
|
||||
DAMAGEIND::ITEM *DAMAGEIND::create_i()
|
||||
{
|
||||
if (num_items < MAX_ITEMS)
|
||||
{
|
||||
ITEM *p = &items[num_items];
|
||||
num_items++;
|
||||
return p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DAMAGEIND::destroy_i(DAMAGEIND::ITEM *i)
|
||||
{
|
||||
num_items--;
|
||||
*i = items[num_items];
|
||||
}
|
||||
|
||||
void DAMAGEIND::create(vec2 pos, vec2 dir)
|
||||
{
|
||||
ITEM *i = create_i();
|
||||
if (i)
|
||||
{
|
||||
i->pos = pos;
|
||||
i->life = 0.75f;
|
||||
i->dir = dir*-1;
|
||||
i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi;
|
||||
}
|
||||
}
|
||||
|
||||
void DAMAGEIND::on_render()
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
for(int i = 0; i < num_items;)
|
||||
{
|
||||
vec2 pos = mix(items[i].pos+items[i].dir*75.0f, items[i].pos, clamp((items[i].life-0.60f)/0.15f, 0.0f, 1.0f));
|
||||
|
||||
items[i].life -= client_frametime();
|
||||
if(items[i].life < 0.0f)
|
||||
destroy_i(&items[i]);
|
||||
else
|
||||
{
|
||||
gfx_setcolor(1.0f,1.0f,1.0f, items[i].life/0.1f);
|
||||
gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f);
|
||||
select_sprite(SPRITE_STAR1);
|
||||
draw_sprite(pos.x, pos.y, 48.0f);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
gfx_quads_end();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class DAMAGEIND : public COMPONENT
|
||||
{
|
||||
int64 lastupdate;
|
||||
struct ITEM
|
||||
{
|
||||
vec2 pos;
|
||||
vec2 dir;
|
||||
float life;
|
||||
float startangle;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_ITEMS=64,
|
||||
};
|
||||
|
||||
ITEM items[MAX_ITEMS];
|
||||
int num_items;
|
||||
|
||||
ITEM *create_i();
|
||||
void destroy_i(ITEM *i);
|
||||
|
||||
public:
|
||||
DAMAGEIND();
|
||||
|
||||
void create(vec2 pos, vec2 dir);
|
||||
virtual void on_render();
|
||||
};
|
||||
@@ -0,0 +1,112 @@
|
||||
#include <memory.h> // memcmp
|
||||
|
||||
extern "C" {
|
||||
#include <engine/e_config.h>
|
||||
}
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/layers.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
//#include "controls.hpp"
|
||||
//#include "camera.hpp"
|
||||
#include "debughud.hpp"
|
||||
|
||||
void DEBUGHUD::render_netcorrections()
|
||||
{
|
||||
if(!config.debug || !gameclient.snap.local_character || !gameclient.snap.local_prev_character)
|
||||
return;
|
||||
|
||||
gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
|
||||
|
||||
/*float speed = distance(vec2(netobjects.local_prev_character->x, netobjects.local_prev_character->y),
|
||||
vec2(netobjects.local_character->x, netobjects.local_character->y));*/
|
||||
|
||||
float velspeed = length(vec2(gameclient.snap.local_character->vx/256.0f, gameclient.snap.local_character->vy/256.0f))*50;
|
||||
|
||||
float ramp = velocity_ramp(velspeed, gameclient.tuning.velramp_start, gameclient.tuning.velramp_range, gameclient.tuning.velramp_curvature);
|
||||
|
||||
char buf[512];
|
||||
str_format(buf, sizeof(buf), "%.0f\n%.0f\n%.2f\n%d %s\n%d %d",
|
||||
velspeed, velspeed*ramp, ramp,
|
||||
netobj_num_corrections(), netobj_corrected_on(),
|
||||
gameclient.snap.local_character->x,
|
||||
gameclient.snap.local_character->y
|
||||
);
|
||||
gfx_text(0, 150, 50, 12, buf, -1);
|
||||
}
|
||||
|
||||
void DEBUGHUD::render_tuning()
|
||||
{
|
||||
// render tuning debugging
|
||||
if(!config.dbg_tuning)
|
||||
return;
|
||||
|
||||
TUNING_PARAMS standard_tuning;
|
||||
|
||||
gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
|
||||
|
||||
float y = 50.0f;
|
||||
int count = 0;
|
||||
for(int i = 0; i < gameclient.tuning.num(); i++)
|
||||
{
|
||||
char buf[128];
|
||||
float current, standard;
|
||||
gameclient.tuning.get(i, ¤t);
|
||||
standard_tuning.get(i, &standard);
|
||||
|
||||
if(standard == current)
|
||||
gfx_text_color(1,1,1,1.0f);
|
||||
else
|
||||
gfx_text_color(1,0.25f,0.25f,1.0f);
|
||||
|
||||
float w;
|
||||
float x = 5.0f;
|
||||
|
||||
str_format(buf, sizeof(buf), "%.2f", standard);
|
||||
x += 20.0f;
|
||||
w = gfx_text_width(0, 5, buf, -1);
|
||||
gfx_text(0x0, x-w, y+count*6, 5, buf, -1);
|
||||
|
||||
str_format(buf, sizeof(buf), "%.2f", current);
|
||||
x += 20.0f;
|
||||
w = gfx_text_width(0, 5, buf, -1);
|
||||
gfx_text(0x0, x-w, y+count*6, 5, buf, -1);
|
||||
|
||||
x += 5.0f;
|
||||
gfx_text(0x0, x, y+count*6, 5, gameclient.tuning.names[i], -1);
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
y = y+count*6;
|
||||
|
||||
gfx_texture_set(-1);
|
||||
gfx_blend_normal();
|
||||
gfx_lines_begin();
|
||||
float height = 50.0f;
|
||||
float pv = 1;
|
||||
for(int i = 0; i < 100; i++)
|
||||
{
|
||||
float speed = i/100.0f * 3000;
|
||||
float ramp = velocity_ramp(speed, gameclient.tuning.velramp_start, gameclient.tuning.velramp_range, gameclient.tuning.velramp_curvature);
|
||||
float rampedspeed = (speed * ramp)/1000.0f;
|
||||
gfx_lines_draw((i-1)*2, y+height-pv*height, i*2, y+height-rampedspeed*height);
|
||||
//gfx_lines_draw((i-1)*2, 200, i*2, 200);
|
||||
pv = rampedspeed;
|
||||
}
|
||||
gfx_lines_end();
|
||||
gfx_text_color(1,1,1,1);
|
||||
}
|
||||
|
||||
void DEBUGHUD::on_render()
|
||||
{
|
||||
render_tuning();
|
||||
render_netcorrections();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class DEBUGHUD : public COMPONENT
|
||||
{
|
||||
void render_netcorrections();
|
||||
void render_tuning();
|
||||
public:
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
//#include <gc_client.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/components/particles.hpp>
|
||||
#include <game/client/components/skins.hpp>
|
||||
#include <game/client/components/flow.hpp>
|
||||
#include <game/client/components/damageind.hpp>
|
||||
#include <game/client/components/sounds.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
|
||||
#include "effects.hpp"
|
||||
|
||||
inline vec2 random_dir() { return normalize(vec2(frandom()-0.5f, frandom()-0.5f)); }
|
||||
|
||||
EFFECTS::EFFECTS()
|
||||
{
|
||||
add_50hz = false;
|
||||
add_100hz = false;
|
||||
}
|
||||
|
||||
void EFFECTS::air_jump(vec2 pos)
|
||||
{
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_AIRJUMP;
|
||||
p.pos = pos + vec2(-6.0f, 16.0f);
|
||||
p.vel = vec2(0, -200);
|
||||
p.life_span = 0.5f;
|
||||
p.start_size = 48.0f;
|
||||
p.end_size = 0;
|
||||
p.rot = frandom()*pi*2;
|
||||
p.rotspeed = pi*2;
|
||||
p.gravity = 500;
|
||||
p.friction = 0.7f;
|
||||
p.flow_affected = 0.0f;
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
|
||||
p.pos = pos + vec2(6.0f, 16.0f);
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
|
||||
gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos);
|
||||
}
|
||||
|
||||
void EFFECTS::damage_indicator(vec2 pos, vec2 dir)
|
||||
{
|
||||
gameclient.damageind->create(pos, dir);
|
||||
}
|
||||
|
||||
void EFFECTS::powerupshine(vec2 pos, vec2 size)
|
||||
{
|
||||
if(!add_50hz)
|
||||
return;
|
||||
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_SLICE;
|
||||
p.pos = pos + vec2((frandom()-0.5f)*size.x, (frandom()-0.5f)*size.y);
|
||||
p.vel = vec2(0, 0);
|
||||
p.life_span = 0.5f;
|
||||
p.start_size = 16.0f;
|
||||
p.end_size = 0;
|
||||
p.rot = frandom()*pi*2;
|
||||
p.rotspeed = pi*2;
|
||||
p.gravity = 500;
|
||||
p.friction = 0.9f;
|
||||
p.flow_affected = 0.0f;
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
}
|
||||
|
||||
void EFFECTS::smoketrail(vec2 pos, vec2 vel)
|
||||
{
|
||||
if(!add_50hz)
|
||||
return;
|
||||
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_SMOKE;
|
||||
p.pos = pos;
|
||||
p.vel = vel + random_dir()*50.0f;
|
||||
p.life_span = 0.5f + frandom()*0.5f;
|
||||
p.start_size = 12.0f + frandom()*8;
|
||||
p.end_size = 0;
|
||||
p.friction = 0.7;
|
||||
p.gravity = frandom()*-500.0f;
|
||||
gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p);
|
||||
}
|
||||
|
||||
|
||||
void EFFECTS::skidtrail(vec2 pos, vec2 vel)
|
||||
{
|
||||
if(!add_100hz)
|
||||
return;
|
||||
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_SMOKE;
|
||||
p.pos = pos;
|
||||
p.vel = vel + random_dir()*50.0f;
|
||||
p.life_span = 0.5f + frandom()*0.5f;
|
||||
p.start_size = 24.0f + frandom()*12;
|
||||
p.end_size = 0;
|
||||
p.friction = 0.7f;
|
||||
p.gravity = frandom()*-500.0f;
|
||||
p.color = vec4(0.75f,0.75f,0.75f,1.0f);
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
}
|
||||
|
||||
void EFFECTS::bullettrail(vec2 pos)
|
||||
{
|
||||
if(!add_100hz)
|
||||
return;
|
||||
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_BALL;
|
||||
p.pos = pos;
|
||||
p.life_span = 0.25f + frandom()*0.25f;
|
||||
p.start_size = 8.0f;
|
||||
p.end_size = 0;
|
||||
p.friction = 0.7f;
|
||||
gameclient.particles->add(PARTICLES::GROUP_PROJECTILE_TRAIL, &p);
|
||||
}
|
||||
|
||||
void EFFECTS::playerspawn(vec2 pos)
|
||||
{
|
||||
for(int i = 0; i < 32; i++)
|
||||
{
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_SHELL;
|
||||
p.pos = pos;
|
||||
p.vel = random_dir() * (pow(frandom(), 3)*600.0f);
|
||||
p.life_span = 0.3f + frandom()*0.3f;
|
||||
p.start_size = 64.0f + frandom()*32;
|
||||
p.end_size = 0;
|
||||
p.rot = frandom()*pi*2;
|
||||
p.rotspeed = frandom();
|
||||
p.gravity = frandom()*-400.0f;
|
||||
p.friction = 0.7f;
|
||||
p.color = vec4(0xb5/255.0f, 0x50/255.0f, 0xcb/255.0f, 1.0f);
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
|
||||
}
|
||||
gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_SPAWN, 1.0f, pos);
|
||||
}
|
||||
|
||||
void EFFECTS::playerdeath(vec2 pos, int cid)
|
||||
{
|
||||
vec3 blood_color(1.0f,1.0f,1.0f);
|
||||
|
||||
if(cid >= 0)
|
||||
{
|
||||
const SKINS::SKIN *s = gameclient.skins->get(gameclient.clients[cid].skin_id);
|
||||
if(s)
|
||||
blood_color = s->blood_color;
|
||||
}
|
||||
|
||||
for(int i = 0; i < 64; i++)
|
||||
{
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_SPLAT01 + (rand()%3);
|
||||
p.pos = pos;
|
||||
p.vel = random_dir() * ((frandom()+0.1f)*900.0f);
|
||||
p.life_span = 0.3f + frandom()*0.3f;
|
||||
p.start_size = 24.0f + frandom()*16;
|
||||
p.end_size = 0;
|
||||
p.rot = frandom()*pi*2;
|
||||
p.rotspeed = (frandom()-0.5f) * pi;
|
||||
p.gravity = 800.0f;
|
||||
p.friction = 0.8f;
|
||||
vec3 c = blood_color * (0.75f + frandom()*0.25f);
|
||||
p.color = vec4(c.r, c.g, c.b, 0.75f);
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EFFECTS::explosion(vec2 pos)
|
||||
{
|
||||
// add to flow
|
||||
for(int y = -8; y <= 8; y++)
|
||||
for(int x = -8; x <= 8; x++)
|
||||
{
|
||||
if(x == 0 && y == 0)
|
||||
continue;
|
||||
|
||||
float a = 1 - (length(vec2(x,y)) / length(vec2(8,8)));
|
||||
gameclient.flow->add(pos+vec2(x,y)*16, normalize(vec2(x,y))*5000.0f*a, 10.0f);
|
||||
}
|
||||
|
||||
// add the explosion
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_EXPL01;
|
||||
p.pos = pos;
|
||||
p.life_span = 0.4f;
|
||||
p.start_size = 150.0f;
|
||||
p.end_size = 0;
|
||||
p.rot = frandom()*pi*2;
|
||||
gameclient.particles->add(PARTICLES::GROUP_EXPLOSIONS, &p);
|
||||
|
||||
// add the smoke
|
||||
for(int i = 0; i < 24; i++)
|
||||
{
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_SMOKE;
|
||||
p.pos = pos;
|
||||
p.vel = random_dir() * ((1.0f + frandom()*0.2f) * 1000.0f);
|
||||
p.life_span = 0.5f + frandom()*0.4f;
|
||||
p.start_size = 32.0f + frandom()*8;
|
||||
p.end_size = 0;
|
||||
p.gravity = frandom()*-800.0f;
|
||||
p.friction = 0.4f;
|
||||
p.color = mix(vec4(0.75f,0.75f,0.75f,1.0f), vec4(0.5f,0.5f,0.5f,1.0f), frandom());
|
||||
gameclient.particles->add(PARTICLES::GROUP_GENERAL, &p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EFFECTS::hammerhit(vec2 pos)
|
||||
{
|
||||
// add the explosion
|
||||
PARTICLE p;
|
||||
p.set_default();
|
||||
p.spr = SPRITE_PART_EXPL01;
|
||||
p.pos = pos;
|
||||
p.life_span = 0.4f;
|
||||
p.start_size = 150.0f;
|
||||
p.end_size = 0;
|
||||
p.rot = frandom()*pi*2;
|
||||
gameclient.particles->add(PARTICLES::GROUP_EXPLOSIONS, &p);
|
||||
gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_HAMMER_HIT, 1.0f, pos);
|
||||
}
|
||||
|
||||
void EFFECTS::on_render()
|
||||
{
|
||||
static int64 last_update_100hz = 0;
|
||||
static int64 last_update_50hz = 0;
|
||||
|
||||
if(time_get()-last_update_100hz > time_freq()/100)
|
||||
{
|
||||
add_100hz = true;
|
||||
last_update_100hz = time_get();
|
||||
}
|
||||
else
|
||||
add_100hz = false;
|
||||
|
||||
if(time_get()-last_update_50hz > time_freq()/100)
|
||||
{
|
||||
add_50hz = true;
|
||||
last_update_50hz = time_get();
|
||||
}
|
||||
else
|
||||
add_50hz = false;
|
||||
|
||||
if(add_50hz)
|
||||
gameclient.flow->update();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class EFFECTS : public COMPONENT
|
||||
{
|
||||
bool add_50hz;
|
||||
bool add_100hz;
|
||||
public:
|
||||
EFFECTS();
|
||||
|
||||
virtual void on_render();
|
||||
|
||||
void bullettrail(vec2 pos);
|
||||
void smoketrail(vec2 pos, vec2 vel);
|
||||
void skidtrail(vec2 pos, vec2 vel);
|
||||
void explosion(vec2 pos);
|
||||
void hammerhit(vec2 pos);
|
||||
void air_jump(vec2 pos);
|
||||
void damage_indicator(vec2 pos, vec2 dir);
|
||||
void playerspawn(vec2 pos);
|
||||
void playerdeath(vec2 pos, int cid);
|
||||
void powerupshine(vec2 pos, vec2 size);
|
||||
|
||||
void update();
|
||||
};
|
||||
@@ -0,0 +1,156 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/gamecore.hpp> // get_angle
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include "emoticon.hpp"
|
||||
|
||||
EMOTICON::EMOTICON()
|
||||
{
|
||||
on_reset();
|
||||
}
|
||||
|
||||
void EMOTICON::con_key_emoticon(void *result, void *user_data)
|
||||
{
|
||||
((EMOTICON *)user_data)->active = console_arg_int(result, 0) != 0;
|
||||
}
|
||||
|
||||
void EMOTICON::con_emote(void *result, void *user_data)
|
||||
{
|
||||
((EMOTICON *)user_data)->emote(console_arg_int(result, 0));
|
||||
}
|
||||
|
||||
void EMOTICON::on_console_init()
|
||||
{
|
||||
MACRO_REGISTER_COMMAND("+emote", "", CFGFLAG_CLIENT, con_key_emoticon, this, "Open emote selector");
|
||||
MACRO_REGISTER_COMMAND("emote", "i", CFGFLAG_CLIENT, con_emote, this, "Use emote");
|
||||
}
|
||||
|
||||
void EMOTICON::on_reset()
|
||||
{
|
||||
was_active = false;
|
||||
active = false;
|
||||
selected_emote = -1;
|
||||
}
|
||||
|
||||
void EMOTICON::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_EMOTICON)
|
||||
{
|
||||
NETMSG_SV_EMOTICON *msg = (NETMSG_SV_EMOTICON *)rawmsg;
|
||||
gameclient.clients[msg->cid].emoticon = msg->emoticon;
|
||||
gameclient.clients[msg->cid].emoticon_start = client_tick();
|
||||
}
|
||||
}
|
||||
|
||||
bool EMOTICON::on_mousemove(float x, float y)
|
||||
{
|
||||
if(!active)
|
||||
return false;
|
||||
|
||||
selector_mouse += vec2(x,y);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EMOTICON::draw_circle(float x, float y, float r, int segments)
|
||||
{
|
||||
float f_segments = (float)segments;
|
||||
for(int i = 0; i < segments; i+=2)
|
||||
{
|
||||
float a1 = i/f_segments * 2*pi;
|
||||
float a2 = (i+1)/f_segments * 2*pi;
|
||||
float a3 = (i+2)/f_segments * 2*pi;
|
||||
float ca1 = cosf(a1);
|
||||
float ca2 = cosf(a2);
|
||||
float ca3 = cosf(a3);
|
||||
float sa1 = sinf(a1);
|
||||
float sa2 = sinf(a2);
|
||||
float sa3 = sinf(a3);
|
||||
|
||||
gfx_quads_draw_freeform(
|
||||
x, y,
|
||||
x+ca1*r, y+sa1*r,
|
||||
x+ca3*r, y+sa3*r,
|
||||
x+ca2*r, y+sa2*r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void EMOTICON::on_render()
|
||||
{
|
||||
if(!active)
|
||||
{
|
||||
if(was_active && selected_emote != -1)
|
||||
emote(selected_emote);
|
||||
was_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
was_active = true;
|
||||
|
||||
int x, y;
|
||||
inp_mouse_relative(&x, &y);
|
||||
|
||||
selector_mouse.x += x;
|
||||
selector_mouse.y += y;
|
||||
|
||||
if (length(selector_mouse) > 140)
|
||||
selector_mouse = normalize(selector_mouse) * 140;
|
||||
|
||||
float selected_angle = get_angle(selector_mouse) + 2*pi/24;
|
||||
if (selected_angle < 0)
|
||||
selected_angle += 2*pi;
|
||||
|
||||
if (length(selector_mouse) > 100)
|
||||
selected_emote = (int)(selected_angle / (2*pi) * 12.0f);
|
||||
|
||||
RECT screen = *ui_screen();
|
||||
|
||||
gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);
|
||||
|
||||
gfx_blend_normal();
|
||||
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0,0,0,0.3f);
|
||||
draw_circle(screen.w/2, screen.h/2, 160, 64);
|
||||
gfx_quads_end();
|
||||
|
||||
gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
float angle = 2*pi*i/12.0;
|
||||
if (angle > pi)
|
||||
angle -= 2*pi;
|
||||
|
||||
bool selected = selected_emote == i;
|
||||
|
||||
float size = selected ? 96 : 64;
|
||||
|
||||
float nudge_x = 120 * cos(angle);
|
||||
float nudge_y = 120 * sin(angle);
|
||||
select_sprite(SPRITE_OOP + i);
|
||||
gfx_quads_draw(screen.w/2 + nudge_x, screen.h/2 + nudge_y, size, size);
|
||||
}
|
||||
|
||||
gfx_quads_end();
|
||||
|
||||
gfx_texture_set(data->images[IMAGE_CURSOR].id);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(1,1,1,1);
|
||||
gfx_quads_drawTL(selector_mouse.x+screen.w/2,selector_mouse.y+screen.h/2,24,24);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void EMOTICON::emote(int emoticon)
|
||||
{
|
||||
NETMSG_CL_EMOTICON msg;
|
||||
msg.emoticon = emoticon;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class EMOTICON : public COMPONENT
|
||||
{
|
||||
void draw_circle(float x, float y, float r, int segments);
|
||||
|
||||
bool was_active;
|
||||
bool active;
|
||||
|
||||
vec2 selector_mouse;
|
||||
int selected_emote;
|
||||
|
||||
static void con_key_emoticon(void *result, void *user_data);
|
||||
static void con_emote(void *result, void *user_data);
|
||||
|
||||
public:
|
||||
EMOTICON();
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_console_init();
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
virtual bool on_mousemove(float x, float y);
|
||||
|
||||
void emote(int emoticon);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
#include <game/mapitems.hpp>
|
||||
#include <game/layers.hpp>
|
||||
#include "flow.hpp"
|
||||
|
||||
FLOW::FLOW()
|
||||
{
|
||||
cells = 0;
|
||||
height = 0;
|
||||
width = 0;
|
||||
spacing = 16;
|
||||
}
|
||||
|
||||
void FLOW::dbg_render()
|
||||
{
|
||||
if(!cells)
|
||||
return;
|
||||
|
||||
gfx_texture_set(-1);
|
||||
gfx_lines_begin();
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
vec2 pos(x*spacing, y*spacing);
|
||||
vec2 vel = cells[y*width+x].vel * 0.01f;
|
||||
gfx_lines_draw(pos.x, pos.y, pos.x+vel.x, pos.y+vel.y);
|
||||
}
|
||||
|
||||
gfx_lines_end();
|
||||
}
|
||||
|
||||
void FLOW::init()
|
||||
{
|
||||
if(cells)
|
||||
{
|
||||
mem_free(cells);
|
||||
cells = 0;
|
||||
}
|
||||
|
||||
MAPITEM_LAYER_TILEMAP *tilemap = layers_game_layer();
|
||||
width = tilemap->width*32/spacing;
|
||||
height = tilemap->height*32/spacing;
|
||||
|
||||
// allocate and clear
|
||||
cells = (CELL *)mem_alloc(sizeof(CELL)*width*height, 1);
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
cells[y*width+x].vel = vec2(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void FLOW::update()
|
||||
{
|
||||
if(!cells)
|
||||
return;
|
||||
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
cells[y*width+x].vel *= 0.85f;
|
||||
}
|
||||
|
||||
vec2 FLOW::get(vec2 pos)
|
||||
{
|
||||
if(!cells)
|
||||
return vec2(0,0);
|
||||
|
||||
int x = (int)(pos.x / spacing);
|
||||
int y = (int)(pos.y / spacing);
|
||||
if(x < 0 || y < 0 || x >= width || y >= height)
|
||||
return vec2(0,0);
|
||||
|
||||
return cells[y*width+x].vel;
|
||||
}
|
||||
|
||||
void FLOW::add(vec2 pos, vec2 vel, float size)
|
||||
{
|
||||
if(!cells)
|
||||
return;
|
||||
|
||||
int x = (int)(pos.x / spacing);
|
||||
int y = (int)(pos.y / spacing);
|
||||
if(x < 0 || y < 0 || x >= width || y >= height)
|
||||
return;
|
||||
|
||||
cells[y*width+x].vel += vel;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class FLOW : public COMPONENT
|
||||
{
|
||||
struct CELL
|
||||
{
|
||||
vec2 vel;
|
||||
};
|
||||
|
||||
CELL *cells;
|
||||
int height;
|
||||
int width;
|
||||
int spacing;
|
||||
|
||||
void dbg_render();
|
||||
void init();
|
||||
public:
|
||||
FLOW();
|
||||
|
||||
vec2 get(vec2 pos);
|
||||
void add(vec2 pos, vec2 vel, float size);
|
||||
void update();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
#include <memory.h> // memcmp
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/layers.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
#include "controls.hpp"
|
||||
#include "camera.hpp"
|
||||
#include "hud.hpp"
|
||||
#include "voting.hpp"
|
||||
#include "binds.hpp"
|
||||
|
||||
HUD::HUD()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void HUD::on_reset()
|
||||
{
|
||||
}
|
||||
|
||||
void HUD::render_goals()
|
||||
{
|
||||
// TODO: split this up into these:
|
||||
// render_gametimer
|
||||
// render_suddendeath
|
||||
// render_scorehud
|
||||
// render_warmuptimer
|
||||
|
||||
int gameflags = gameclient.snap.gameobj->flags;
|
||||
|
||||
float whole = 300*gfx_screenaspect();
|
||||
float half = whole/2.0f;
|
||||
|
||||
|
||||
gfx_mapscreen(0,0,300*gfx_screenaspect(),300);
|
||||
if(!gameclient.snap.gameobj->sudden_death)
|
||||
{
|
||||
char buf[32];
|
||||
int time = 0;
|
||||
if(gameclient.snap.gameobj->time_limit)
|
||||
{
|
||||
time = gameclient.snap.gameobj->time_limit*60 - ((client_tick()-gameclient.snap.gameobj->round_start_tick)/client_tickspeed());
|
||||
|
||||
if(gameclient.snap.gameobj->game_over)
|
||||
time = 0;
|
||||
}
|
||||
else
|
||||
time = (client_tick()-gameclient.snap.gameobj->round_start_tick)/client_tickspeed();
|
||||
|
||||
str_format(buf, sizeof(buf), "%d:%02d", time /60, time %60);
|
||||
float w = gfx_text_width(0, 16, buf, -1);
|
||||
gfx_text(0, half-w/2, 2, 16, buf, -1);
|
||||
}
|
||||
|
||||
if(gameclient.snap.gameobj->sudden_death)
|
||||
{
|
||||
const char *text = "Sudden Death";
|
||||
float w = gfx_text_width(0, 16, text, -1);
|
||||
gfx_text(0, half-w/2, 2, 16, text, -1);
|
||||
}
|
||||
|
||||
// render small score hud
|
||||
if(!(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over) && (gameflags&GAMEFLAG_TEAMS))
|
||||
{
|
||||
for(int t = 0; t < 2; t++)
|
||||
{
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
if(t == 0)
|
||||
gfx_setcolor(1,0,0,0.25f);
|
||||
else
|
||||
gfx_setcolor(0,0,1,0.25f);
|
||||
draw_round_rect(whole-40, 300-40-15+t*20, 50, 18, 5.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
char buf[32];
|
||||
str_format(buf, sizeof(buf), "%d", t?gameclient.snap.gameobj->teamscore_blue:gameclient.snap.gameobj->teamscore_red);
|
||||
float w = gfx_text_width(0, 14, buf, -1);
|
||||
|
||||
if(gameflags&GAMEFLAG_FLAGS)
|
||||
{
|
||||
gfx_text(0, whole-20-w/2+5, 300-40-15+t*20, 14, buf, -1);
|
||||
if(gameclient.snap.flags[t])
|
||||
{
|
||||
if(gameclient.snap.flags[t]->carried_by == -2 || (gameclient.snap.flags[t]->carried_by == -1 && ((client_tick()/10)&1)))
|
||||
{
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
if(t == 0) select_sprite(SPRITE_FLAG_RED);
|
||||
else select_sprite(SPRITE_FLAG_BLUE);
|
||||
|
||||
float size = 16;
|
||||
gfx_quads_drawTL(whole-40+5, 300-40-15+t*20+1, size/2, size);
|
||||
gfx_quads_end();
|
||||
}
|
||||
else if(gameclient.snap.flags[t]->carried_by >= 0)
|
||||
{
|
||||
int id = gameclient.snap.flags[t]->carried_by%MAX_CLIENTS;
|
||||
const char *name = gameclient.clients[id].name;
|
||||
float w = gfx_text_width(0, 10, name, -1);
|
||||
gfx_text(0, whole-40-5-w, 300-40-15+t*20+2, 10, name, -1);
|
||||
TEE_RENDER_INFO info = gameclient.clients[id].render_info;
|
||||
info.size = 18.0f;
|
||||
|
||||
render_tee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0),
|
||||
vec2(whole-40+10, 300-40-15+9+t*20+1));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
gfx_text(0, whole-20-w/2, 300-40-15+t*20, 14, buf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// render warmup timer
|
||||
if(gameclient.snap.gameobj->warmup)
|
||||
{
|
||||
char buf[256];
|
||||
float w = gfx_text_width(0, 24, "Warmup", -1);
|
||||
gfx_text(0, 150*gfx_screenaspect()+-w/2, 50, 24, "Warmup", -1);
|
||||
|
||||
int seconds = gameclient.snap.gameobj->warmup/SERVER_TICK_SPEED;
|
||||
if(seconds < 5)
|
||||
str_format(buf, sizeof(buf), "%d.%d", seconds, (gameclient.snap.gameobj->warmup*10/SERVER_TICK_SPEED)%10);
|
||||
else
|
||||
str_format(buf, sizeof(buf), "%d", seconds);
|
||||
w = gfx_text_width(0, 24, buf, -1);
|
||||
gfx_text(0, 150*gfx_screenaspect()+-w/2, 75, 24, buf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group)
|
||||
{
|
||||
float points[4];
|
||||
mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f,
|
||||
group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points);
|
||||
gfx_mapscreen(points[0], points[1], points[2], points[3]);
|
||||
}
|
||||
|
||||
void HUD::render_fps()
|
||||
{
|
||||
if(config.cl_showfps)
|
||||
{
|
||||
char buf[512];
|
||||
str_format(buf, sizeof(buf), "%d", (int)(1.0f/client_frametime()));
|
||||
gfx_text(0, width-10-gfx_text_width(0,12,buf,-1), 5, 12, buf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void HUD::render_connectionwarning()
|
||||
{
|
||||
if(client_connection_problems())
|
||||
{
|
||||
const char *text = "Connection Problems...";
|
||||
float w = gfx_text_width(0, 24, text, -1);
|
||||
gfx_text(0, 150*gfx_screenaspect()-w/2, 50, 24, text, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void HUD::render_teambalancewarning()
|
||||
{
|
||||
// render prompt about team-balance
|
||||
bool flash = time_get()/(time_freq()/2)%2 == 0;
|
||||
if (gameclient.snap.gameobj && (gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) != 0)
|
||||
{
|
||||
if (config.cl_warning_teambalance && abs(gameclient.snap.team_size[0]-gameclient.snap.team_size[1]) >= 2)
|
||||
{
|
||||
const char *text = "Please balance teams!";
|
||||
if(flash)
|
||||
gfx_text_color(1,1,0.5f,1);
|
||||
else
|
||||
gfx_text_color(0.7f,0.7f,0.2f,1.0f);
|
||||
gfx_text(0x0, 5, 50, 6, text, -1);
|
||||
gfx_text_color(1,1,1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HUD::render_voting()
|
||||
{
|
||||
if(!gameclient.voting->is_voting())
|
||||
return;
|
||||
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0,0,0,0.40f);
|
||||
draw_round_rect(-10, 60-2, 100+10+4+5, 28, 5.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
gfx_text_color(1,1,1,1);
|
||||
|
||||
char buf[512];
|
||||
gfx_text(0x0, 5, 60, 6, gameclient.voting->vote_description(), -1);
|
||||
|
||||
str_format(buf, sizeof(buf), "%ds left", gameclient.voting->seconds_left());
|
||||
float tw = gfx_text_width(0x0, 6, buf, -1);
|
||||
gfx_text(0x0, 5+100-tw, 60, 6, buf, -1);
|
||||
|
||||
|
||||
RECT base = {5, 70, 100, 4};
|
||||
gameclient.voting->render_bars(base, false);
|
||||
|
||||
const char *yes_key = gameclient.binds->get_key("vote yes");
|
||||
const char *no_key = gameclient.binds->get_key("vote no");
|
||||
str_format(buf, sizeof(buf), "%s - Vote Yes", yes_key);
|
||||
base.y += base.h+1;
|
||||
ui_do_label(&base, buf, 6.0f, -1);
|
||||
|
||||
str_format(buf, sizeof(buf), "Vote No - %s", no_key);
|
||||
ui_do_label(&base, buf, 6.0f, 1);
|
||||
}
|
||||
|
||||
void HUD::render_cursor()
|
||||
{
|
||||
if(!gameclient.snap.local_character)
|
||||
return;
|
||||
|
||||
mapscreen_to_group(gameclient.camera->center.x, gameclient.camera->center.y, layers_game_group());
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
// render cursor
|
||||
select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_cursor);
|
||||
float cursorsize = 64;
|
||||
draw_sprite(gameclient.controls->target_pos.x, gameclient.controls->target_pos.y, cursorsize);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void HUD::render_healthandammo()
|
||||
{
|
||||
//mapscreen_to_group(gacenter_x, center_y, layers_game_group());
|
||||
|
||||
float x = 5;
|
||||
float y = 5;
|
||||
|
||||
// render ammo count
|
||||
// render gui stuff
|
||||
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_mapscreen(0,0,width,300);
|
||||
|
||||
gfx_quads_begin();
|
||||
|
||||
// if weaponstage is active, put a "glow" around the stage ammo
|
||||
select_sprite(data->weapons.id[gameclient.snap.local_character->weapon%NUM_WEAPONS].sprite_proj);
|
||||
for (int i = 0; i < min(gameclient.snap.local_character->ammocount, 10); i++)
|
||||
gfx_quads_drawTL(x+i*12,y+24,10,10);
|
||||
|
||||
gfx_quads_end();
|
||||
|
||||
gfx_quads_begin();
|
||||
int h = 0;
|
||||
|
||||
// render health
|
||||
select_sprite(SPRITE_HEALTH_FULL);
|
||||
for(; h < gameclient.snap.local_character->health; h++)
|
||||
gfx_quads_drawTL(x+h*12,y,10,10);
|
||||
|
||||
select_sprite(SPRITE_HEALTH_EMPTY);
|
||||
for(; h < 10; h++)
|
||||
gfx_quads_drawTL(x+h*12,y,10,10);
|
||||
|
||||
// render armor meter
|
||||
h = 0;
|
||||
select_sprite(SPRITE_ARMOR_FULL);
|
||||
for(; h < gameclient.snap.local_character->armor; h++)
|
||||
gfx_quads_drawTL(x+h*12,y+12,10,10);
|
||||
|
||||
select_sprite(SPRITE_ARMOR_EMPTY);
|
||||
for(; h < 10; h++)
|
||||
gfx_quads_drawTL(x+h*12,y+12,10,10);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void HUD::on_render()
|
||||
{
|
||||
if(!gameclient.snap.gameobj)
|
||||
return;
|
||||
|
||||
width = 300*gfx_screenaspect();
|
||||
|
||||
bool spectate = false;
|
||||
if(gameclient.snap.local_info && gameclient.snap.local_info->team == -1)
|
||||
spectate = true;
|
||||
|
||||
if(gameclient.snap.local_character && !spectate && !(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over))
|
||||
render_healthandammo();
|
||||
|
||||
render_goals();
|
||||
render_fps();
|
||||
if(client_state() != CLIENTSTATE_DEMOPLAYBACK)
|
||||
render_connectionwarning();
|
||||
render_teambalancewarning();
|
||||
render_voting();
|
||||
render_cursor();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class HUD : public COMPONENT
|
||||
{
|
||||
float width;
|
||||
|
||||
void render_cursor();
|
||||
|
||||
void render_fps();
|
||||
void render_connectionwarning();
|
||||
void render_teambalancewarning();
|
||||
void render_voting();
|
||||
void render_healthandammo();
|
||||
void render_goals();
|
||||
|
||||
public:
|
||||
HUD();
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/gamecore.hpp> // get_angle
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
#include <game/client/components/flow.hpp>
|
||||
#include <game/client/components/effects.hpp>
|
||||
|
||||
#include "items.hpp"
|
||||
|
||||
void ITEMS::render_projectile(const NETOBJ_PROJECTILE *current, int itemid)
|
||||
{
|
||||
|
||||
// get positions
|
||||
float curvature = 0;
|
||||
float speed = 0;
|
||||
if(current->type == WEAPON_GRENADE)
|
||||
{
|
||||
curvature = gameclient.tuning.grenade_curvature;
|
||||
speed = gameclient.tuning.grenade_speed;
|
||||
}
|
||||
else if(current->type == WEAPON_SHOTGUN)
|
||||
{
|
||||
curvature = gameclient.tuning.shotgun_curvature;
|
||||
speed = gameclient.tuning.shotgun_speed;
|
||||
}
|
||||
else if(current->type == WEAPON_GUN)
|
||||
{
|
||||
curvature = gameclient.tuning.gun_curvature;
|
||||
speed = gameclient.tuning.gun_speed;
|
||||
}
|
||||
|
||||
float ct = (client_prevtick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime();
|
||||
if(ct < 0)
|
||||
return; // projectile havn't been shot yet
|
||||
|
||||
vec2 startpos(current->x, current->y);
|
||||
vec2 startvel(current->vx/100.0f, current->vy/100.0f);
|
||||
vec2 pos = calc_pos(startpos, startvel, curvature, speed, ct);
|
||||
vec2 prevpos = calc_pos(startpos, startvel, curvature, speed, ct-0.001f);
|
||||
|
||||
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
select_sprite(data->weapons.id[clamp(current->type, 0, NUM_WEAPONS-1)].sprite_proj);
|
||||
vec2 vel = pos-prevpos;
|
||||
//vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
|
||||
|
||||
|
||||
// add particle for this projectile
|
||||
if(current->type == WEAPON_GRENADE)
|
||||
{
|
||||
gameclient.effects->smoketrail(pos, vel*-1);
|
||||
gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f);
|
||||
gfx_quads_setrotation(client_localtime()*pi*2*2 + itemid);
|
||||
}
|
||||
else
|
||||
{
|
||||
gameclient.effects->bullettrail(pos);
|
||||
gameclient.flow->add(pos, vel*1000*client_frametime(), 10.0f);
|
||||
|
||||
if(length(vel) > 0.00001f)
|
||||
gfx_quads_setrotation(get_angle(vel));
|
||||
else
|
||||
gfx_quads_setrotation(0);
|
||||
|
||||
}
|
||||
|
||||
gfx_quads_draw(pos.x, pos.y, 32, 32);
|
||||
gfx_quads_setrotation(0);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void ITEMS::render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *current)
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
|
||||
float angle = 0.0f;
|
||||
float size = 64.0f;
|
||||
if (current->type == POWERUP_WEAPON)
|
||||
{
|
||||
angle = 0; //-pi/6;//-0.25f * pi * 2.0f;
|
||||
select_sprite(data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].sprite_body);
|
||||
size = data->weapons.id[clamp(current->subtype, 0, NUM_WEAPONS-1)].visual_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int c[] = {
|
||||
SPRITE_PICKUP_HEALTH,
|
||||
SPRITE_PICKUP_ARMOR,
|
||||
SPRITE_PICKUP_WEAPON,
|
||||
SPRITE_PICKUP_NINJA
|
||||
};
|
||||
select_sprite(c[current->type]);
|
||||
|
||||
if(c[current->type] == SPRITE_PICKUP_NINJA)
|
||||
{
|
||||
gameclient.effects->powerupshine(pos, vec2(96,18));
|
||||
size *= 2.0f;
|
||||
pos.x += 10.0f;
|
||||
}
|
||||
}
|
||||
|
||||
gfx_quads_setrotation(angle);
|
||||
|
||||
float offset = pos.y/32.0f + pos.x/32.0f;
|
||||
pos.x += cosf(client_localtime()*2.0f+offset)*2.5f;
|
||||
pos.y += sinf(client_localtime()*2.0f+offset)*2.5f;
|
||||
draw_sprite(pos.x, pos.y, size);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void ITEMS::render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current)
|
||||
{
|
||||
float angle = 0.0f;
|
||||
float size = 42.0f;
|
||||
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
if(current->team == 0) // red team
|
||||
select_sprite(SPRITE_FLAG_RED);
|
||||
else
|
||||
select_sprite(SPRITE_FLAG_BLUE);
|
||||
|
||||
gfx_quads_setrotation(angle);
|
||||
|
||||
vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
|
||||
|
||||
// make sure that the flag isn't interpolated between capture and return
|
||||
if(prev->carried_by != current->carried_by)
|
||||
pos = vec2(current->x, current->y);
|
||||
|
||||
// make sure to use predicted position if we are the carrier
|
||||
if(gameclient.snap.local_info && current->carried_by == gameclient.snap.local_info->cid)
|
||||
pos = gameclient.local_character_pos;
|
||||
|
||||
gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
|
||||
void ITEMS::render_laser(const struct NETOBJ_LASER *current)
|
||||
{
|
||||
vec2 pos = vec2(current->x, current->y);
|
||||
vec2 from = vec2(current->from_x, current->from_y);
|
||||
vec2 dir = normalize(pos-from);
|
||||
|
||||
float ticks = client_tick() + client_intratick() - current->start_tick;
|
||||
float ms = (ticks/50.0f) * 1000.0f;
|
||||
float a = ms / gameclient.tuning.laser_bounce_delay;
|
||||
a = clamp(a, 0.0f, 1.0f);
|
||||
float ia = 1-a;
|
||||
|
||||
vec2 out, border;
|
||||
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
|
||||
//vec4 inner_color(0.15f,0.35f,0.75f,1.0f);
|
||||
//vec4 outer_color(0.65f,0.85f,1.0f,1.0f);
|
||||
|
||||
// do outline
|
||||
vec4 outer_color(0.075f,0.075f,0.25f,1.0f);
|
||||
gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f);
|
||||
out = vec2(dir.y, -dir.x) * (7.0f*ia);
|
||||
|
||||
gfx_quads_draw_freeform(
|
||||
from.x-out.x, from.y-out.y,
|
||||
from.x+out.x, from.y+out.y,
|
||||
pos.x-out.x, pos.y-out.y,
|
||||
pos.x+out.x, pos.y+out.y
|
||||
);
|
||||
|
||||
// do inner
|
||||
vec4 inner_color(0.5f,0.5f,1.0f,1.0f);
|
||||
out = vec2(dir.y, -dir.x) * (5.0f*ia);
|
||||
gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f); // center
|
||||
|
||||
gfx_quads_draw_freeform(
|
||||
from.x-out.x, from.y-out.y,
|
||||
from.x+out.x, from.y+out.y,
|
||||
pos.x-out.x, pos.y-out.y,
|
||||
pos.x+out.x, pos.y+out.y
|
||||
);
|
||||
|
||||
gfx_quads_end();
|
||||
|
||||
// render head
|
||||
{
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(data->images[IMAGE_PARTICLES].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
int sprites[] = {SPRITE_PART_SPLAT01, SPRITE_PART_SPLAT02, SPRITE_PART_SPLAT03};
|
||||
select_sprite(sprites[client_tick()%3]);
|
||||
gfx_quads_setrotation(client_tick());
|
||||
gfx_setcolor(outer_color.r,outer_color.g,outer_color.b,1.0f);
|
||||
gfx_quads_draw(pos.x, pos.y, 24,24);
|
||||
gfx_setcolor(inner_color.r, inner_color.g, inner_color.b, 1.0f);
|
||||
gfx_quads_draw(pos.x, pos.y, 20,20);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
gfx_blend_normal();
|
||||
}
|
||||
|
||||
void ITEMS::on_render()
|
||||
{
|
||||
int num = snap_num_items(SNAP_CURRENT);
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
|
||||
if(item.type == NETOBJTYPE_PROJECTILE)
|
||||
{
|
||||
render_projectile((const NETOBJ_PROJECTILE *)data, item.id);
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_PICKUP)
|
||||
{
|
||||
const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
|
||||
if(prev)
|
||||
render_pickup((const NETOBJ_PICKUP *)prev, (const NETOBJ_PICKUP *)data);
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_LASER)
|
||||
{
|
||||
render_laser((const NETOBJ_LASER *)data);
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_FLAG)
|
||||
{
|
||||
const void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
|
||||
if (prev)
|
||||
render_flag((const NETOBJ_FLAG *)prev, (const NETOBJ_FLAG *)data);
|
||||
}
|
||||
}
|
||||
|
||||
// render extra projectiles
|
||||
/*
|
||||
for(int i = 0; i < extraproj_num; i++)
|
||||
{
|
||||
if(extraproj_projectiles[i].start_tick < client_tick())
|
||||
{
|
||||
extraproj_projectiles[i] = extraproj_projectiles[extraproj_num-1];
|
||||
extraproj_num--;
|
||||
}
|
||||
else
|
||||
render_projectile(&extraproj_projectiles[i], 0);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class ITEMS : public COMPONENT
|
||||
{
|
||||
void render_projectile(const NETOBJ_PROJECTILE *current, int itemid);
|
||||
void render_pickup(const NETOBJ_PICKUP *prev, const NETOBJ_PICKUP *current);
|
||||
void render_flag(const NETOBJ_FLAG *prev, const NETOBJ_FLAG *current);
|
||||
void render_laser(const struct NETOBJ_LASER *current);
|
||||
|
||||
public:
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
#include "killmessages.hpp"
|
||||
|
||||
void KILLMESSAGES::on_reset()
|
||||
{
|
||||
killmsg_current = 0;
|
||||
for(int i = 0; i < killmsg_max; i++)
|
||||
killmsgs[i].tick = -100000;
|
||||
}
|
||||
|
||||
void KILLMESSAGES::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_KILLMSG)
|
||||
{
|
||||
NETMSG_SV_KILLMSG *msg = (NETMSG_SV_KILLMSG *)rawmsg;
|
||||
|
||||
// unpack messages
|
||||
KILLMSG kill;
|
||||
kill.killer = msg->killer;
|
||||
kill.victim = msg->victim;
|
||||
kill.weapon = msg->weapon;
|
||||
kill.mode_special = msg->mode_special;
|
||||
kill.tick = client_tick();
|
||||
|
||||
// add the message
|
||||
killmsg_current = (killmsg_current+1)%killmsg_max;
|
||||
killmsgs[killmsg_current] = kill;
|
||||
}
|
||||
}
|
||||
|
||||
void KILLMESSAGES::on_render()
|
||||
{
|
||||
float width = 400*3.0f*gfx_screenaspect();
|
||||
float height = 400*3.0f;
|
||||
|
||||
gfx_mapscreen(0, 0, width*1.5f, height*1.5f);
|
||||
float startx = width*1.5f-10.0f;
|
||||
float y = 20.0f;
|
||||
|
||||
for(int i = 0; i < killmsg_max; i++)
|
||||
{
|
||||
|
||||
int r = (killmsg_current+i+1)%killmsg_max;
|
||||
if(client_tick() > killmsgs[r].tick+50*10)
|
||||
continue;
|
||||
|
||||
float font_size = 36.0f;
|
||||
float killername_w = gfx_text_width(0, font_size, gameclient.clients[killmsgs[r].killer].name, -1);
|
||||
float victimname_w = gfx_text_width(0, font_size, gameclient.clients[killmsgs[r].victim].name, -1);
|
||||
|
||||
float x = startx;
|
||||
|
||||
// render victim name
|
||||
x -= victimname_w;
|
||||
gfx_text(0, x, y, font_size, gameclient.clients[killmsgs[r].victim].name, -1);
|
||||
|
||||
// render victim tee
|
||||
x -= 24.0f;
|
||||
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_FLAGS)
|
||||
{
|
||||
if(killmsgs[r].mode_special&1)
|
||||
{
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
if(gameclient.clients[killmsgs[r].victim].team == 0) select_sprite(SPRITE_FLAG_BLUE);
|
||||
else select_sprite(SPRITE_FLAG_RED);
|
||||
|
||||
float size = 56.0f;
|
||||
gfx_quads_drawTL(x, y-16, size/2, size);
|
||||
gfx_quads_end();
|
||||
}
|
||||
}
|
||||
|
||||
render_tee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].victim].render_info, EMOTE_PAIN, vec2(-1,0), vec2(x, y+28));
|
||||
x -= 32.0f;
|
||||
|
||||
// render weapon
|
||||
x -= 44.0f;
|
||||
if (killmsgs[r].weapon >= 0)
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
select_sprite(data->weapons.id[killmsgs[r].weapon].sprite_body);
|
||||
draw_sprite(x, y+28, 96);
|
||||
gfx_quads_end();
|
||||
}
|
||||
x -= 52.0f;
|
||||
|
||||
if(killmsgs[r].victim != killmsgs[r].killer)
|
||||
{
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_FLAGS)
|
||||
{
|
||||
if(killmsgs[r].mode_special&2)
|
||||
{
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
if(gameclient.clients[killmsgs[r].killer].team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X);
|
||||
else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X);
|
||||
|
||||
float size = 56.0f;
|
||||
gfx_quads_drawTL(x-56, y-16, size/2, size);
|
||||
gfx_quads_end();
|
||||
}
|
||||
}
|
||||
|
||||
// render killer tee
|
||||
x -= 24.0f;
|
||||
render_tee(ANIMSTATE::get_idle(), &gameclient.clients[killmsgs[r].killer].render_info, EMOTE_ANGRY, vec2(1,0), vec2(x, y+28));
|
||||
x -= 32.0f;
|
||||
|
||||
// render killer name
|
||||
x -= killername_w;
|
||||
gfx_text(0, x, y, font_size, gameclient.clients[killmsgs[r].killer].name, -1);
|
||||
}
|
||||
|
||||
y += 44;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class KILLMESSAGES : public COMPONENT
|
||||
{
|
||||
public:
|
||||
// kill messages
|
||||
struct KILLMSG
|
||||
{
|
||||
int weapon;
|
||||
int victim;
|
||||
int killer;
|
||||
int mode_special; // for CTF, if the guy is carrying a flag for example
|
||||
int tick;
|
||||
};
|
||||
|
||||
static const int killmsg_max = 5;
|
||||
KILLMSG killmsgs[killmsg_max];
|
||||
int killmsg_current;
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/mapitems.hpp>
|
||||
|
||||
#include "mapimages.hpp"
|
||||
|
||||
MAPIMAGES::MAPIMAGES()
|
||||
{
|
||||
count = 0;
|
||||
}
|
||||
|
||||
void MAPIMAGES::on_mapload()
|
||||
{
|
||||
// unload all textures
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
gfx_unload_texture(textures[i]);
|
||||
textures[i] = -1;
|
||||
}
|
||||
count = 0;
|
||||
|
||||
int start;
|
||||
map_get_type(MAPITEMTYPE_IMAGE, &start, &count);
|
||||
|
||||
// load new textures
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
textures[i] = 0;
|
||||
|
||||
MAPITEM_IMAGE *img = (MAPITEM_IMAGE *)map_get_item(start+i, 0, 0);
|
||||
if(img->external)
|
||||
{
|
||||
char buf[256];
|
||||
char *name = (char *)map_get_data(img->image_name);
|
||||
str_format(buf, sizeof(buf), "mapres/%s.png", name);
|
||||
textures[i] = gfx_load_texture(buf, IMG_AUTO, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
void *data = map_get_data(img->image_data);
|
||||
textures[i] = gfx_load_texture_raw(img->width, img->height, IMG_RGBA, data, IMG_RGBA, 0);
|
||||
map_unload_data(img->image_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class MAPIMAGES : public COMPONENT
|
||||
{
|
||||
int textures[64];
|
||||
int count;
|
||||
public:
|
||||
MAPIMAGES();
|
||||
|
||||
int get(int index) const { return textures[index]; }
|
||||
int num() const { return count; }
|
||||
|
||||
virtual void on_mapload();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
#include <game/layers.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
#include <game/client/components/camera.hpp>
|
||||
#include <game/client/components/mapimages.hpp>
|
||||
|
||||
#include "maplayers.hpp"
|
||||
|
||||
MAPLAYERS::MAPLAYERS(int t)
|
||||
{
|
||||
type = t;
|
||||
}
|
||||
|
||||
|
||||
static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *group)
|
||||
{
|
||||
float points[4];
|
||||
mapscreen_to_world(center_x, center_y, group->parallax_x/100.0f, group->parallax_y/100.0f,
|
||||
group->offset_x, group->offset_y, gfx_screenaspect(), 1.0f, points);
|
||||
gfx_mapscreen(points[0], points[1], points[2], points[3]);
|
||||
}
|
||||
|
||||
static void envelope_eval(float time_offset, int env, float *channels)
|
||||
{
|
||||
channels[0] = 0;
|
||||
channels[1] = 0;
|
||||
channels[2] = 0;
|
||||
channels[3] = 0;
|
||||
|
||||
ENVPOINT *points;
|
||||
|
||||
{
|
||||
int start, num;
|
||||
map_get_type(MAPITEMTYPE_ENVPOINTS, &start, &num);
|
||||
if(num)
|
||||
points = (ENVPOINT *)map_get_item(start, 0, 0);
|
||||
}
|
||||
|
||||
int start, num;
|
||||
map_get_type(MAPITEMTYPE_ENVELOPE, &start, &num);
|
||||
|
||||
if(env >= num)
|
||||
return;
|
||||
|
||||
MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)map_get_item(start+env, 0, 0);
|
||||
render_eval_envelope(points+item->start_point, item->num_points, 4, client_localtime()+time_offset, channels);
|
||||
}
|
||||
|
||||
void MAPLAYERS::on_render()
|
||||
{
|
||||
if(client_state() != CLIENTSTATE_ONLINE && client_state() != CLIENTSTATE_DEMOPLAYBACK)
|
||||
return;
|
||||
|
||||
RECT screen;
|
||||
gfx_getscreen(&screen.x, &screen.y, &screen.w, &screen.h);
|
||||
|
||||
vec2 center = gameclient.camera->center;
|
||||
//float center_x = gameclient.camera->center.x;
|
||||
//float center_y = gameclient.camera->center.y;
|
||||
|
||||
bool passed_gamelayer = false;
|
||||
|
||||
for(int g = 0; g < layers_num_groups(); g++)
|
||||
{
|
||||
MAPITEM_GROUP *group = layers_get_group(g);
|
||||
|
||||
if(!config.gfx_noclip && group->version >= 2 && group->use_clipping)
|
||||
{
|
||||
// set clipping
|
||||
float points[4];
|
||||
mapscreen_to_group(center.x, center.y, layers_game_group());
|
||||
gfx_getscreen(&points[0], &points[1], &points[2], &points[3]);
|
||||
float x0 = (group->clip_x - points[0]) / (points[2]-points[0]);
|
||||
float y0 = (group->clip_y - points[1]) / (points[3]-points[1]);
|
||||
float x1 = ((group->clip_x+group->clip_w) - points[0]) / (points[2]-points[0]);
|
||||
float y1 = ((group->clip_y+group->clip_h) - points[1]) / (points[3]-points[1]);
|
||||
|
||||
gfx_clip_enable((int)(x0*gfx_screenwidth()), (int)(y0*gfx_screenheight()),
|
||||
(int)((x1-x0)*gfx_screenwidth()), (int)((y1-y0)*gfx_screenheight()));
|
||||
}
|
||||
|
||||
mapscreen_to_group(center.x, center.y, group);
|
||||
|
||||
for(int l = 0; l < group->num_layers; l++)
|
||||
{
|
||||
MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l);
|
||||
bool render = false;
|
||||
bool is_game_layer = false;
|
||||
|
||||
if(layer == (MAPITEM_LAYER*)layers_game_layer())
|
||||
{
|
||||
is_game_layer = true;
|
||||
passed_gamelayer = 1;
|
||||
}
|
||||
|
||||
// skip rendering if detail layers if not wanted
|
||||
if(layer->flags&LAYERFLAG_DETAIL && !config.gfx_high_detail && !is_game_layer)
|
||||
continue;
|
||||
|
||||
if(type == -1)
|
||||
render = true;
|
||||
else if(type == 0)
|
||||
{
|
||||
if(passed_gamelayer)
|
||||
return;
|
||||
render = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(passed_gamelayer && !is_game_layer)
|
||||
render = true;
|
||||
}
|
||||
|
||||
if(render && !is_game_layer)
|
||||
{
|
||||
//layershot_begin();
|
||||
|
||||
if(layer->type == LAYERTYPE_TILES)
|
||||
{
|
||||
MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer;
|
||||
if(tmap->image == -1)
|
||||
gfx_texture_set(-1);
|
||||
else
|
||||
gfx_texture_set(gameclient.mapimages->get(tmap->image));
|
||||
|
||||
TILE *tiles = (TILE *)map_get_data(tmap->data);
|
||||
gfx_blend_none();
|
||||
render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_OPAQUE);
|
||||
gfx_blend_normal();
|
||||
render_tilemap(tiles, tmap->width, tmap->height, 32.0f, vec4(1,1,1,1), TILERENDERFLAG_EXTEND|LAYERRENDERFLAG_TRANSPARENT);
|
||||
}
|
||||
else if(layer->type == LAYERTYPE_QUADS)
|
||||
{
|
||||
MAPITEM_LAYER_QUADS *qlayer = (MAPITEM_LAYER_QUADS *)layer;
|
||||
if(qlayer->image == -1)
|
||||
gfx_texture_set(-1);
|
||||
else
|
||||
gfx_texture_set(gameclient.mapimages->get(qlayer->image));
|
||||
|
||||
QUAD *quads = (QUAD *)map_get_data_swapped(qlayer->data);
|
||||
|
||||
gfx_blend_none();
|
||||
render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_OPAQUE);
|
||||
gfx_blend_normal();
|
||||
render_quads(quads, qlayer->num_quads, envelope_eval, LAYERRENDERFLAG_TRANSPARENT);
|
||||
}
|
||||
|
||||
//layershot_end();
|
||||
}
|
||||
}
|
||||
if(!config.gfx_noclip)
|
||||
gfx_clip_disable();
|
||||
}
|
||||
|
||||
if(!config.gfx_noclip)
|
||||
gfx_clip_disable();
|
||||
|
||||
// reset the screen like it was before
|
||||
gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class MAPLAYERS : public COMPONENT
|
||||
{
|
||||
int type;
|
||||
public:
|
||||
enum
|
||||
{
|
||||
TYPE_BACKGROUND=0,
|
||||
TYPE_FOREGROUND,
|
||||
};
|
||||
|
||||
MAPLAYERS(int type);
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,183 @@
|
||||
#include <base/vmath.hpp>
|
||||
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/client/ui.hpp>
|
||||
|
||||
|
||||
// compnent to fetch keypresses, override all other input
|
||||
class MENUS_KEYBINDER : public COMPONENT
|
||||
{
|
||||
public:
|
||||
bool take_key;
|
||||
bool got_key;
|
||||
INPUT_EVENT key;
|
||||
MENUS_KEYBINDER();
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
|
||||
class MENUS : public COMPONENT
|
||||
{
|
||||
static vec4 gui_color;
|
||||
static vec4 color_tabbar_inactive_outgame;
|
||||
static vec4 color_tabbar_active_outgame;
|
||||
static vec4 color_tabbar_inactive_ingame;
|
||||
static vec4 color_tabbar_active_ingame;
|
||||
static vec4 color_tabbar_inactive;
|
||||
static vec4 color_tabbar_active;
|
||||
|
||||
static vec4 button_color_mul(const void *id);
|
||||
|
||||
|
||||
static void ui_draw_demoplayer_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
|
||||
static void ui_draw_browse_icon(int what, const RECT *r);
|
||||
static void ui_draw_menu_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_keyselect_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_menu_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_settings_tab_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_grid_header(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_list_row(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_checkbox_common(const void *id, const char *text, const char *boxtext, const RECT *r);
|
||||
static void ui_draw_checkbox(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static void ui_draw_checkbox_number(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
static int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false);
|
||||
|
||||
static float ui_do_scrollbar_v(const void *id, const RECT *rect, float current);
|
||||
static float ui_do_scrollbar_h(const void *id, const RECT *rect, float current);
|
||||
|
||||
static int ui_do_key_reader(void *id, const RECT *rect, int key);
|
||||
static void ui_do_getbuttons(int start, int stop, RECT view);
|
||||
|
||||
struct LISTBOXITEM
|
||||
{
|
||||
int visible;
|
||||
int selected;
|
||||
RECT rect;
|
||||
};
|
||||
|
||||
static void ui_do_listbox_start(void *id, const RECT *rect, float row_height, const char *title, int num_items, int selected_index);
|
||||
static LISTBOXITEM ui_do_listbox_nextitem(void *id);
|
||||
static int ui_do_listbox_end();
|
||||
|
||||
//static void demolist_listdir_callback(const char *name, int is_dir, void *user);
|
||||
//static void demolist_list_callback(const RECT *rect, int index, void *user);
|
||||
|
||||
enum
|
||||
{
|
||||
POPUP_NONE=0,
|
||||
POPUP_FIRST_LAUNCH,
|
||||
POPUP_CONNECTING,
|
||||
POPUP_DISCONNECTED,
|
||||
POPUP_PURE,
|
||||
POPUP_PASSWORD,
|
||||
POPUP_QUIT,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PAGE_NEWS=1,
|
||||
PAGE_GAME,
|
||||
PAGE_SERVER_INFO,
|
||||
PAGE_CALLVOTE,
|
||||
PAGE_INTERNET,
|
||||
PAGE_LAN,
|
||||
PAGE_FAVORITES,
|
||||
PAGE_DEMOS,
|
||||
PAGE_SETTINGS,
|
||||
PAGE_SYSTEM,
|
||||
};
|
||||
|
||||
int game_page;
|
||||
int popup;
|
||||
int active_page;
|
||||
bool menu_active;
|
||||
vec2 mouse_pos;
|
||||
|
||||
int64 last_input;
|
||||
|
||||
// TODO: this is a bit ugly but.. well.. yeah
|
||||
enum { MAX_INPUTEVENTS = 32 };
|
||||
static INPUT_EVENT inputevents[MAX_INPUTEVENTS];
|
||||
static int num_inputevents;
|
||||
|
||||
// some settings
|
||||
static float button_height;
|
||||
static float listheader_height;
|
||||
static float fontmod_height;
|
||||
|
||||
// for graphic settings
|
||||
bool need_restart;
|
||||
bool need_sendinfo;
|
||||
|
||||
//
|
||||
bool escape_pressed;
|
||||
bool enter_pressed;
|
||||
|
||||
// for call vote
|
||||
int callvote_selectedoption;
|
||||
int callvote_selectedplayer;
|
||||
|
||||
// demo
|
||||
struct DEMOITEM
|
||||
{
|
||||
char filename[512];
|
||||
char name[256];
|
||||
};
|
||||
|
||||
DEMOITEM *demos;
|
||||
int num_demos;
|
||||
|
||||
void demolist_populate();
|
||||
static void demolist_count_callback(const char *name, int is_dir, void *user);
|
||||
static void demolist_fetch_callback(const char *name, int is_dir, void *user);
|
||||
|
||||
// found in menus.cpp
|
||||
int render();
|
||||
void render_background();
|
||||
//void render_loading(float percent);
|
||||
int render_menubar(RECT r);
|
||||
void render_news(RECT main_view);
|
||||
|
||||
// found in menus_demo.cpp
|
||||
void render_demoplayer(RECT main_view);
|
||||
void render_demolist(RECT main_view);
|
||||
|
||||
// found in menus_ingame.cpp
|
||||
void render_game(RECT main_view);
|
||||
void render_serverinfo(RECT main_view);
|
||||
void render_servercontrol(RECT main_view);
|
||||
void render_servercontrol_kick(RECT main_view);
|
||||
void render_servercontrol_server(RECT main_view);
|
||||
|
||||
// found in menus_browser.cpp
|
||||
int selected_index;
|
||||
void render_serverbrowser_serverlist(RECT view);
|
||||
void render_serverbrowser_serverdetail(RECT view);
|
||||
void render_serverbrowser_filters(RECT view);
|
||||
void render_serverbrowser(RECT main_view);
|
||||
|
||||
// found in menus_settings.cpp
|
||||
void render_settings_player(RECT main_view);
|
||||
void render_settings_controls(RECT main_view);
|
||||
void render_settings_graphics(RECT main_view);
|
||||
void render_settings_sound(RECT main_view);
|
||||
void render_settings(RECT main_view);
|
||||
|
||||
void set_active(bool active);
|
||||
public:
|
||||
static MENUS_KEYBINDER binder;
|
||||
|
||||
MENUS();
|
||||
|
||||
void render_loading(float percent);
|
||||
|
||||
bool is_active() const { return menu_active; }
|
||||
|
||||
void init();
|
||||
|
||||
virtual void on_statechange(int new_state, int old_state);
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
virtual bool on_mousemove(float x, float y);
|
||||
};
|
||||
@@ -0,0 +1,669 @@
|
||||
|
||||
#include <string.h> // strcmp, strlen, strncpy
|
||||
#include <stdlib.h> // atoi
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
extern "C" {
|
||||
#include <engine/client/ec_font.h>
|
||||
}
|
||||
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include "menus.hpp"
|
||||
#include <game/version.hpp>
|
||||
|
||||
void MENUS::render_serverbrowser_serverlist(RECT view)
|
||||
{
|
||||
RECT headers;
|
||||
RECT status;
|
||||
|
||||
ui_hsplit_t(&view, listheader_height, &headers, &view);
|
||||
ui_hsplit_b(&view, 28.0f, &view, &status);
|
||||
|
||||
// split of the scrollbar
|
||||
ui_draw_rect(&headers, vec4(1,1,1,0.25f), CORNER_T, 5.0f);
|
||||
ui_vsplit_r(&headers, 20.0f, &headers, 0);
|
||||
|
||||
struct column
|
||||
{
|
||||
int id;
|
||||
int sort;
|
||||
const char *caption;
|
||||
int direction;
|
||||
float width;
|
||||
int flags;
|
||||
RECT rect;
|
||||
RECT spacer;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FIXED=1,
|
||||
SPACER=2,
|
||||
|
||||
COL_FLAG_LOCK=0,
|
||||
COL_FLAG_PURE,
|
||||
COL_FLAG_FAV,
|
||||
COL_NAME,
|
||||
COL_GAMETYPE,
|
||||
COL_MAP,
|
||||
COL_PLAYERS,
|
||||
COL_PING,
|
||||
COL_PROGRESS,
|
||||
COL_VERSION,
|
||||
};
|
||||
|
||||
static column cols[] = {
|
||||
{-1, -1, " ", -1, 2.0f, 0, {0}, {0}},
|
||||
{COL_FLAG_LOCK, -1, " ", -1, 14.0f, 0, {0}, {0}},
|
||||
{COL_FLAG_PURE, -1, " ", -1, 14.0f, 0, {0}, {0}},
|
||||
{COL_FLAG_FAV, -1, " ", -1, 14.0f, 0, {0}, {0}},
|
||||
{COL_NAME, BROWSESORT_NAME, "Name", 0, 300.0f, 0, {0}, {0}},
|
||||
{COL_GAMETYPE, BROWSESORT_GAMETYPE, "Type", 1, 50.0f, 0, {0}, {0}},
|
||||
{COL_MAP, BROWSESORT_MAP, "Map", 1, 100.0f, 0, {0}, {0}},
|
||||
{COL_PLAYERS, BROWSESORT_NUMPLAYERS, "Players", 1, 60.0f, 0, {0}, {0}},
|
||||
{-1, -1, " ", 1, 10.0f, 0, {0}, {0}},
|
||||
{COL_PING, BROWSESORT_PING, "Ping", 1, 40.0f, FIXED, {0}, {0}},
|
||||
};
|
||||
|
||||
int num_cols = sizeof(cols)/sizeof(column);
|
||||
|
||||
// do layout
|
||||
for(int i = 0; i < num_cols; i++)
|
||||
{
|
||||
if(cols[i].direction == -1)
|
||||
{
|
||||
ui_vsplit_l(&headers, cols[i].width, &cols[i].rect, &headers);
|
||||
|
||||
if(i+1 < num_cols)
|
||||
{
|
||||
//cols[i].flags |= SPACER;
|
||||
ui_vsplit_l(&headers, 2, &cols[i].spacer, &headers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = num_cols-1; i >= 0; i--)
|
||||
{
|
||||
if(cols[i].direction == 1)
|
||||
{
|
||||
ui_vsplit_r(&headers, cols[i].width, &headers, &cols[i].rect);
|
||||
ui_vsplit_r(&headers, 2, &headers, &cols[i].spacer);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < num_cols; i++)
|
||||
{
|
||||
if(cols[i].direction == 0)
|
||||
cols[i].rect = headers;
|
||||
}
|
||||
|
||||
// do headers
|
||||
for(int i = 0; i < num_cols; i++)
|
||||
{
|
||||
if(ui_do_button(cols[i].caption, cols[i].caption, config.b_sort == cols[i].sort, &cols[i].rect, ui_draw_grid_header, 0))
|
||||
{
|
||||
if(cols[i].sort != -1)
|
||||
{
|
||||
if(config.b_sort == cols[i].sort)
|
||||
config.b_sort_order ^= 1;
|
||||
else
|
||||
config.b_sort_order = 0;
|
||||
config.b_sort = cols[i].sort;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0);
|
||||
|
||||
RECT scroll;
|
||||
ui_vsplit_r(&view, 15, &view, &scroll);
|
||||
|
||||
int num_servers = client_serverbrowse_sorted_num();
|
||||
|
||||
// display important messages in the middle of the screen so no
|
||||
// users misses it
|
||||
{
|
||||
RECT msgbox = view;
|
||||
msgbox.y += view.h/3;
|
||||
|
||||
if(active_page == PAGE_INTERNET && client_serverbrowse_refreshingmasters())
|
||||
ui_do_label(&msgbox, "Refreshing master servers", 16.0f, 0);
|
||||
else if(!client_serverbrowse_num())
|
||||
ui_do_label(&msgbox, "No servers found", 16.0f, 0);
|
||||
else if(client_serverbrowse_num() && !num_servers)
|
||||
ui_do_label(&msgbox, "No servers match your filter criteria", 16.0f, 0);
|
||||
}
|
||||
|
||||
int num = (int)(view.h/cols[0].rect.h);
|
||||
static int scrollbar = 0;
|
||||
static float scrollvalue = 0;
|
||||
//static int selected_index = -1;
|
||||
ui_hmargin(&scroll, 5.0f, &scroll);
|
||||
scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue);
|
||||
|
||||
int scrollnum = num_servers-num+10;
|
||||
if(scrollnum > 0)
|
||||
{
|
||||
if(inp_key_presses(KEY_MOUSE_WHEEL_UP))
|
||||
scrollvalue -= 1.0f/scrollnum;
|
||||
if(inp_key_presses(KEY_MOUSE_WHEEL_DOWN))
|
||||
scrollvalue += 1.0f/scrollnum;
|
||||
|
||||
if(scrollvalue < 0) scrollvalue = 0;
|
||||
if(scrollvalue > 1) scrollvalue = 1;
|
||||
}
|
||||
else
|
||||
scrollnum = 0;
|
||||
|
||||
// set clipping
|
||||
ui_clip_enable(&view);
|
||||
|
||||
int start = (int)(scrollnum*scrollvalue);
|
||||
if(start < 0)
|
||||
start = 0;
|
||||
|
||||
RECT original_view = view;
|
||||
view.y -= scrollvalue*scrollnum*cols[0].rect.h;
|
||||
|
||||
int new_selected = -1;
|
||||
int num_players = 0;
|
||||
|
||||
selected_index = -1;
|
||||
|
||||
for (int i = 0; i < num_servers; i++)
|
||||
{
|
||||
SERVER_INFO *item = client_serverbrowse_sorted_get(i);
|
||||
num_players += item->num_players;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_servers; i++)
|
||||
{
|
||||
int item_index = i;
|
||||
SERVER_INFO *item = client_serverbrowse_sorted_get(item_index);
|
||||
RECT row;
|
||||
RECT select_hit_box;
|
||||
|
||||
int selected = strcmp(item->address, config.ui_server_address) == 0; //selected_index==item_index;
|
||||
|
||||
ui_hsplit_t(&view, 17.0f, &row, &view);
|
||||
select_hit_box = row;
|
||||
|
||||
if(selected)
|
||||
{
|
||||
selected_index = i;
|
||||
RECT r = row;
|
||||
ui_margin(&r, 1.5f, &r);
|
||||
ui_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f);
|
||||
}
|
||||
|
||||
|
||||
// make sure that only those in view can be selected
|
||||
if(row.y+row.h > original_view.y)
|
||||
{
|
||||
if(select_hit_box.y < original_view.y) // clip the selection
|
||||
{
|
||||
select_hit_box.h -= original_view.y-select_hit_box.y;
|
||||
select_hit_box.y = original_view.y;
|
||||
}
|
||||
|
||||
if(ui_do_button(item, "", selected, &select_hit_box, 0, 0))
|
||||
{
|
||||
new_selected = item_index;
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to do more
|
||||
if(row.y > original_view.y+original_view.h && !ui_active_item())
|
||||
break;
|
||||
|
||||
for(int c = 0; c < num_cols; c++)
|
||||
{
|
||||
RECT button;
|
||||
char temp[64];
|
||||
button.x = cols[c].rect.x;
|
||||
button.y = row.y;
|
||||
button.h = row.h;
|
||||
button.w = cols[c].rect.w;
|
||||
|
||||
//int s = 0;
|
||||
int id = cols[c].id;
|
||||
|
||||
//s = ui_do_button(item, "L", l, &button, ui_draw_browse_icon, 0);
|
||||
|
||||
if(id == COL_FLAG_LOCK)
|
||||
{
|
||||
if(item->flags & SRVFLAG_PASSWORD)
|
||||
ui_draw_browse_icon(SPRITE_BROWSE_LOCK, &button);
|
||||
}
|
||||
else if(id == COL_FLAG_PURE)
|
||||
{
|
||||
if(strcmp(item->gametype, "DM") == 0 || strcmp(item->gametype, "TDM") == 0 || strcmp(item->gametype, "CTF") == 0)
|
||||
{
|
||||
// pure server
|
||||
}
|
||||
else
|
||||
{
|
||||
// unpure
|
||||
ui_draw_browse_icon(SPRITE_BROWSE_UNPURE, &button);
|
||||
}
|
||||
}
|
||||
else if(id == COL_FLAG_FAV)
|
||||
{
|
||||
if(item->favorite)
|
||||
ui_draw_browse_icon(SPRITE_BROWSE_HEART, &button);
|
||||
}
|
||||
else if(id == COL_NAME)
|
||||
{
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, button.x, button.y, 12.0f*ui_scale(), TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
|
||||
cursor.line_width = button.w;
|
||||
|
||||
if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_SERVERNAME))
|
||||
{
|
||||
// highlight the parts that matches
|
||||
const char *s = str_find_nocase(item->name, config.b_filter_string);
|
||||
if(s)
|
||||
{
|
||||
gfx_text_ex(&cursor, item->name, (int)(s-item->name));
|
||||
gfx_text_color(0.4f,0.4f,1.0f,1);
|
||||
gfx_text_ex(&cursor, s, strlen(config.b_filter_string));
|
||||
gfx_text_color(1,1,1,1);
|
||||
gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1);
|
||||
}
|
||||
else
|
||||
gfx_text_ex(&cursor, item->name, -1);
|
||||
}
|
||||
else
|
||||
gfx_text_ex(&cursor, item->name, -1);
|
||||
}
|
||||
else if(id == COL_MAP)
|
||||
ui_do_label(&button, item->map, 12.0f, -1);
|
||||
else if(id == COL_PLAYERS)
|
||||
{
|
||||
str_format(temp, sizeof(temp), "%i/%i", item->num_players, item->max_players);
|
||||
if(config.b_filter_string[0] && (item->quicksearch_hit&BROWSEQUICK_PLAYERNAME))
|
||||
gfx_text_color(0.4f,0.4f,1.0f,1);
|
||||
ui_do_label(&button, temp, 12.0f, 1);
|
||||
gfx_text_color(1,1,1,1);
|
||||
}
|
||||
else if(id == COL_PING)
|
||||
{
|
||||
str_format(temp, sizeof(temp), "%i", item->latency);
|
||||
ui_do_label(&button, temp, 12.0f, 1);
|
||||
}
|
||||
else if(id == COL_PROGRESS)
|
||||
{
|
||||
if(item->progression > 100)
|
||||
item->progression = 100;
|
||||
ui_draw_browse_icon(item->progression, &button);
|
||||
}
|
||||
else if(id == COL_VERSION)
|
||||
{
|
||||
const char *version = item->version;
|
||||
if(strcmp(version, "0.3 e2d7973c6647a13c") == 0) // TODO: remove me later on
|
||||
version = "0.3.0";
|
||||
ui_do_label(&button, version, 12.0f, 1);
|
||||
}
|
||||
else if(id == COL_GAMETYPE)
|
||||
{
|
||||
ui_do_label(&button, item->gametype, 12.0f, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ui_clip_disable();
|
||||
|
||||
if(new_selected != -1)
|
||||
{
|
||||
// select the new server
|
||||
SERVER_INFO *item = client_serverbrowse_sorted_get(new_selected);
|
||||
strncpy(config.ui_server_address, item->address, sizeof(config.ui_server_address));
|
||||
if(inp_mouse_doubleclick())
|
||||
client_connect(config.ui_server_address);
|
||||
}
|
||||
|
||||
ui_draw_rect(&status, vec4(1,1,1,0.25f), CORNER_B, 5.0f);
|
||||
ui_margin(&status, 5.0f, &status);
|
||||
|
||||
// render quick search
|
||||
RECT quicksearch, button;
|
||||
ui_vsplit_l(&status, 250.0f, &quicksearch, &status);
|
||||
ui_do_label(&quicksearch, "Quick search: ", 14.0f, -1);
|
||||
ui_vsplit_l(&quicksearch, gfx_text_width(0, 14.0f, "Quick search: ", -1), 0, &quicksearch);
|
||||
ui_do_edit_box(&config.b_filter_string, &quicksearch, config.b_filter_string, sizeof(config.b_filter_string), 14.0f);
|
||||
ui_vsplit_l(&quicksearch, 135.0f, &quicksearch, &button);
|
||||
ui_vmargin(&button, 0.0f, &button);
|
||||
static int clear_button = 0;
|
||||
if(ui_do_button(&clear_button, "x", 0, &button, ui_draw_menu_button, 0))
|
||||
config.b_filter_string[0] = 0;
|
||||
|
||||
// render status
|
||||
char buf[128];
|
||||
str_format(buf, sizeof(buf), "%d of %d servers, %d players", client_serverbrowse_sorted_num(), client_serverbrowse_num(), num_players);
|
||||
ui_vsplit_r(&status, gfx_text_width(0, 14.0f, buf, -1), 0, &status);
|
||||
ui_do_label(&status, buf, 14.0f, -1);
|
||||
}
|
||||
|
||||
void MENUS::render_serverbrowser_filters(RECT view)
|
||||
{
|
||||
// filters
|
||||
RECT button;
|
||||
|
||||
ui_hsplit_t(&view, 5.0f, 0, &view);
|
||||
ui_vsplit_l(&view, 5.0f, 0, &view);
|
||||
ui_vsplit_r(&view, 5.0f, &view, 0);
|
||||
ui_hsplit_b(&view, 5.0f, &view, 0);
|
||||
|
||||
// render filters
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
if (ui_do_button(&config.b_filter_empty, "Has people playing", config.b_filter_empty, &button, ui_draw_checkbox, 0))
|
||||
config.b_filter_empty ^= 1;
|
||||
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
if (ui_do_button(&config.b_filter_full, "Server not full", config.b_filter_full, &button, ui_draw_checkbox, 0))
|
||||
config.b_filter_full ^= 1;
|
||||
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
if (ui_do_button(&config.b_filter_pw, "No password", config.b_filter_pw, &button, ui_draw_checkbox, 0))
|
||||
config.b_filter_pw ^= 1;
|
||||
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
if (ui_do_button((char *)&config.b_filter_compatversion, "Compatible Version", config.b_filter_compatversion, &button, ui_draw_checkbox, 0))
|
||||
config.b_filter_compatversion ^= 1;
|
||||
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
if (ui_do_button((char *)&config.b_filter_pure, "Standard gametype", config.b_filter_pure, &button, ui_draw_checkbox, 0))
|
||||
config.b_filter_pure ^= 1;
|
||||
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
/*ui_vsplit_l(&button, 20.0f, 0, &button);*/
|
||||
if (ui_do_button((char *)&config.b_filter_pure_map, "Standard map", config.b_filter_pure_map, &button, ui_draw_checkbox, 0))
|
||||
config.b_filter_pure_map ^= 1;
|
||||
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
ui_do_label(&button, "Game types: ", 14.0f, -1);
|
||||
ui_vsplit_l(&button, 95.0f, 0, &button);
|
||||
ui_margin(&button, 1.0f, &button);
|
||||
ui_do_edit_box(&config.b_filter_gametype, &button, config.b_filter_gametype, sizeof(config.b_filter_gametype), 14.0f);
|
||||
|
||||
{
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
RECT editbox;
|
||||
ui_vsplit_l(&button, 40.0f, &editbox, &button);
|
||||
ui_vsplit_l(&button, 5.0f, &button, &button);
|
||||
|
||||
char buf[8];
|
||||
str_format(buf, sizeof(buf), "%d", config.b_filter_ping);
|
||||
ui_do_edit_box(&config.b_filter_ping, &editbox, buf, sizeof(buf), 14.0f);
|
||||
config.b_filter_ping = atoi(buf);
|
||||
|
||||
ui_do_label(&button, "Maximum ping", 14.0f, -1);
|
||||
}
|
||||
|
||||
ui_hsplit_b(&view, button_height, &view, &button);
|
||||
static int clear_button = 0;
|
||||
if(ui_do_button(&clear_button, "Reset Filter", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
config.b_filter_full = 0;
|
||||
config.b_filter_empty = 0;
|
||||
config.b_filter_pw = 0;
|
||||
config.b_filter_ping = 999;
|
||||
config.b_filter_gametype[0] = 0;
|
||||
config.b_filter_compatversion = 1;
|
||||
config.b_filter_string[0] = 0;
|
||||
config.b_filter_pure = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void MENUS::render_serverbrowser_serverdetail(RECT view)
|
||||
{
|
||||
RECT server_details = view;
|
||||
RECT server_scoreboard, server_header;
|
||||
|
||||
SERVER_INFO *selected_server = client_serverbrowse_sorted_get(selected_index);
|
||||
|
||||
//ui_vsplit_l(&server_details, 10.0f, 0x0, &server_details);
|
||||
|
||||
// split off a piece to use for scoreboard
|
||||
ui_hsplit_t(&server_details, 140.0f, &server_details, &server_scoreboard);
|
||||
ui_hsplit_b(&server_details, 10.0f, &server_details, 0x0);
|
||||
|
||||
// server details
|
||||
const float font_size = 12.0f;
|
||||
ui_hsplit_t(&server_details, 20.0f, &server_header, &server_details);
|
||||
ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f);
|
||||
ui_draw_rect(&server_details, vec4(0,0,0,0.15f), CORNER_B, 4.0f);
|
||||
ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header);
|
||||
ui_do_label(&server_header, "Server Details: ", font_size+2.0f, -1);
|
||||
|
||||
ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details);
|
||||
|
||||
ui_margin(&server_details, 3.0f, &server_details);
|
||||
|
||||
if (selected_server)
|
||||
{
|
||||
RECT row;
|
||||
static const char *labels[] = { "Version:", "Game Type:", "Progression:", "Ping:" };
|
||||
|
||||
RECT left_column;
|
||||
RECT right_column;
|
||||
|
||||
//
|
||||
{
|
||||
RECT button;
|
||||
ui_hsplit_b(&server_details, 20.0f, &server_details, &button);
|
||||
static int add_fav_button = 0;
|
||||
if (ui_do_button(&add_fav_button, "Favorite", selected_server->favorite, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
if(selected_server->favorite)
|
||||
client_serverbrowse_removefavorite(selected_server->netaddr);
|
||||
else
|
||||
client_serverbrowse_addfavorite(selected_server->netaddr);
|
||||
}
|
||||
}
|
||||
//ui_do_label(&row, temp, font_size, -1);
|
||||
|
||||
ui_vsplit_l(&server_details, 5.0f, 0x0, &server_details);
|
||||
ui_vsplit_l(&server_details, 80.0f, &left_column, &right_column);
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(labels) / sizeof(labels[0]); i++)
|
||||
{
|
||||
ui_hsplit_t(&left_column, 15.0f, &row, &left_column);
|
||||
ui_do_label(&row, labels[i], font_size, -1);
|
||||
}
|
||||
|
||||
ui_hsplit_t(&right_column, 15.0f, &row, &right_column);
|
||||
ui_do_label(&row, selected_server->version, font_size, -1);
|
||||
|
||||
ui_hsplit_t(&right_column, 15.0f, &row, &right_column);
|
||||
ui_do_label(&row, selected_server->gametype, font_size, -1);
|
||||
|
||||
char temp[16];
|
||||
|
||||
if(selected_server->progression < 0)
|
||||
str_format(temp, sizeof(temp), "N/A");
|
||||
else
|
||||
str_format(temp, sizeof(temp), "%d%%", selected_server->progression);
|
||||
ui_hsplit_t(&right_column, 15.0f, &row, &right_column);
|
||||
ui_do_label(&row, temp, font_size, -1);
|
||||
|
||||
str_format(temp, sizeof(temp), "%d", selected_server->latency);
|
||||
ui_hsplit_t(&right_column, 15.0f, &row, &right_column);
|
||||
ui_do_label(&row, temp, font_size, -1);
|
||||
|
||||
}
|
||||
|
||||
// server scoreboard
|
||||
|
||||
ui_hsplit_b(&server_scoreboard, 10.0f, &server_scoreboard, 0x0);
|
||||
ui_hsplit_t(&server_scoreboard, 20.0f, &server_header, &server_scoreboard);
|
||||
ui_draw_rect(&server_header, vec4(1,1,1,0.25f), CORNER_T, 4.0f);
|
||||
ui_draw_rect(&server_scoreboard, vec4(0,0,0,0.15f), CORNER_B, 4.0f);
|
||||
ui_vsplit_l(&server_header, 8.0f, 0x0, &server_header);
|
||||
ui_do_label(&server_header, "Scoreboard: ", font_size+2.0f, -1);
|
||||
|
||||
ui_vsplit_l(&server_scoreboard, 5.0f, 0x0, &server_scoreboard);
|
||||
|
||||
ui_margin(&server_scoreboard, 3.0f, &server_scoreboard);
|
||||
|
||||
if (selected_server)
|
||||
{
|
||||
for (int i = 0; i < selected_server->num_players; i++)
|
||||
{
|
||||
RECT row;
|
||||
char temp[16];
|
||||
ui_hsplit_t(&server_scoreboard, 16.0f, &row, &server_scoreboard);
|
||||
|
||||
str_format(temp, sizeof(temp), "%d", selected_server->players[i].score);
|
||||
ui_do_label(&row, temp, font_size, -1);
|
||||
|
||||
ui_vsplit_l(&row, 25.0f, 0x0, &row);
|
||||
|
||||
TEXT_CURSOR cursor;
|
||||
gfx_text_set_cursor(&cursor, row.x, row.y, 12.0f, TEXTFLAG_RENDER);
|
||||
|
||||
const char *name = selected_server->players[i].name;
|
||||
if(config.b_filter_string[0])
|
||||
{
|
||||
// highlight the parts that matches
|
||||
const char *s = str_find_nocase(name, config.b_filter_string);
|
||||
if(s)
|
||||
{
|
||||
gfx_text_ex(&cursor, name, (int)(s-name));
|
||||
gfx_text_color(0.4f,0.4f,1,1);
|
||||
gfx_text_ex(&cursor, s, strlen(config.b_filter_string));
|
||||
gfx_text_color(1,1,1,1);
|
||||
gfx_text_ex(&cursor, s+strlen(config.b_filter_string), -1);
|
||||
}
|
||||
else
|
||||
gfx_text_ex(&cursor, name, -1);
|
||||
}
|
||||
else
|
||||
gfx_text_ex(&cursor, name, -1);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MENUS::render_serverbrowser(RECT main_view)
|
||||
{
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f);
|
||||
|
||||
RECT view;
|
||||
ui_margin(&main_view, 10.0f, &view);
|
||||
|
||||
/*
|
||||
+-----------------+ +------+
|
||||
| | | |
|
||||
| | | tool |
|
||||
| | | box |
|
||||
| | | |
|
||||
| | +------+
|
||||
+-----------------+ button
|
||||
status toolbar box
|
||||
*/
|
||||
|
||||
|
||||
//RECT filters;
|
||||
RECT status_toolbar;
|
||||
RECT toolbox;
|
||||
RECT button_box;
|
||||
|
||||
// split off a piece for filters, details and scoreboard
|
||||
ui_vsplit_r(&view, 200.0f, &view, &toolbox);
|
||||
ui_hsplit_b(&toolbox, 80.0f, &toolbox, &button_box);
|
||||
ui_hsplit_b(&view, button_height+5.0f, &view, &status_toolbar);
|
||||
|
||||
render_serverbrowser_serverlist(view);
|
||||
|
||||
int toolbox_page = config.ui_toolbox_page;
|
||||
|
||||
ui_vsplit_l(&toolbox, 5.0f, 0, &toolbox);
|
||||
|
||||
// do tabbar
|
||||
{
|
||||
RECT tab_bar;
|
||||
RECT tabbutton0, tabbutton1;
|
||||
ui_hsplit_t(&toolbox, 22.0f, &tab_bar, &toolbox);
|
||||
|
||||
ui_vsplit_mid(&tab_bar, &tabbutton0, &tabbutton1);
|
||||
ui_vsplit_r(&tabbutton0, 5.0f, &tabbutton0, 0);
|
||||
ui_vsplit_l(&tabbutton1, 5.0f, 0, &tabbutton1);
|
||||
|
||||
static int filters_tab = 0;
|
||||
if (ui_do_button(&filters_tab, "Filter", toolbox_page==0, &tabbutton0, ui_draw_menu_tab_button, 0))
|
||||
toolbox_page = 0;
|
||||
|
||||
static int info_tab = 0;
|
||||
if (ui_do_button(&info_tab, "Info", toolbox_page==1, &tabbutton1, ui_draw_menu_tab_button, 0))
|
||||
toolbox_page = 1;
|
||||
}
|
||||
|
||||
config.ui_toolbox_page = toolbox_page;
|
||||
|
||||
ui_draw_rect(&toolbox, vec4(0,0,0,0.15f), 0, 0);
|
||||
|
||||
ui_hsplit_t(&toolbox, 5.0f, 0, &toolbox);
|
||||
|
||||
if(toolbox_page == 0)
|
||||
render_serverbrowser_filters(toolbox);
|
||||
else if(toolbox_page == 1)
|
||||
render_serverbrowser_serverdetail(toolbox);
|
||||
|
||||
{
|
||||
ui_hsplit_t(&status_toolbar, 5.0f, 0, &status_toolbar);
|
||||
|
||||
RECT button;
|
||||
//ui_vsplit_r(&buttons, 20.0f, &buttons, &button);
|
||||
ui_vsplit_r(&status_toolbar, 100.0f, &status_toolbar, &button);
|
||||
ui_vmargin(&button, 2.0f, &button);
|
||||
static int refresh_button = 0;
|
||||
if(ui_do_button(&refresh_button, "Refresh", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
if(config.ui_page == PAGE_INTERNET)
|
||||
client_serverbrowse_refresh(BROWSETYPE_INTERNET);
|
||||
else if(config.ui_page == PAGE_LAN)
|
||||
client_serverbrowse_refresh(BROWSETYPE_LAN);
|
||||
else if(config.ui_page == PAGE_FAVORITES)
|
||||
client_serverbrowse_refresh(BROWSETYPE_FAVORITES);
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
if(strcmp(client_latestversion(), "0") != 0)
|
||||
str_format(buf, sizeof(buf), "Teeworlds %s is out! Download it at www.teeworlds.com!\nCurrent version: %s", client_latestversion(), GAME_VERSION);
|
||||
else
|
||||
str_format(buf, sizeof(buf), "Current version: %s", GAME_VERSION);
|
||||
ui_do_label(&status_toolbar, buf, 14.0f, -1);
|
||||
}
|
||||
|
||||
// do the button box
|
||||
{
|
||||
|
||||
ui_vsplit_l(&button_box, 5.0f, 0, &button_box);
|
||||
ui_vsplit_r(&button_box, 5.0f, &button_box, 0);
|
||||
|
||||
RECT button;
|
||||
ui_hsplit_b(&button_box, button_height, &button_box, &button);
|
||||
ui_vsplit_r(&button, 120.0f, 0, &button);
|
||||
ui_vmargin(&button, 2.0f, &button);
|
||||
//ui_vmargin(&button, 2.0f, &button);
|
||||
static int join_button = 0;
|
||||
if(ui_do_button(&join_button, "Connect", 0, &button, ui_draw_menu_button, 0) || enter_pressed)
|
||||
{
|
||||
client_connect(config.ui_server_address);
|
||||
enter_pressed = false;
|
||||
}
|
||||
|
||||
ui_hsplit_b(&button_box, 5.0f, &button_box, &button);
|
||||
ui_hsplit_b(&button_box, 20.0f, &button_box, &button);
|
||||
ui_do_edit_box(&config.ui_server_address, &button, config.ui_server_address, sizeof(config.ui_server_address), 14.0f);
|
||||
ui_hsplit_b(&button_box, 20.0f, &button_box, &button);
|
||||
ui_do_label(&button, "Host address:", 14.0f, -1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
|
||||
#include <base/math.hpp>
|
||||
|
||||
//#include <string.h> // strcmp, strlen, strncpy
|
||||
//#include <stdlib.h> // atoi
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/client/render.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
|
||||
//#include <game/generated/g_protocol.hpp>
|
||||
//#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/ui.hpp>
|
||||
//#include <game/client/gameclient.hpp>
|
||||
//#include <game/client/animstate.hpp>
|
||||
|
||||
#include "menus.hpp"
|
||||
|
||||
void MENUS::ui_draw_demoplayer_button(const void *id, const char *text, int checked, const RECT *r, const void *extra)
|
||||
{
|
||||
ui_draw_rect(r, vec4(1,1,1, checked ? 0.10f : 0.5f)*button_color_mul(id), CORNER_ALL, 5.0f);
|
||||
ui_do_label(r, text, 14.0f, 0);
|
||||
}
|
||||
|
||||
void MENUS::render_demoplayer(RECT main_view)
|
||||
{
|
||||
const DEMOPLAYBACK_INFO *info = client_demoplayer_getinfo();
|
||||
|
||||
const float seekbar_height = 15.0f;
|
||||
const float buttonbar_height = 20.0f;
|
||||
const float margins = 5.0f;
|
||||
float total_height;
|
||||
|
||||
if(menu_active)
|
||||
total_height = seekbar_height+buttonbar_height+margins*3;
|
||||
else
|
||||
total_height = seekbar_height+margins*2;
|
||||
|
||||
ui_hsplit_b(&main_view, total_height, 0, &main_view);
|
||||
ui_vsplit_l(&main_view, 250.0f, 0, &main_view);
|
||||
ui_vsplit_r(&main_view, 250.0f, &main_view, 0);
|
||||
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_T, 10.0f);
|
||||
|
||||
ui_margin(&main_view, 5.0f, &main_view);
|
||||
|
||||
RECT seekbar, buttonbar;
|
||||
|
||||
if(menu_active)
|
||||
{
|
||||
ui_hsplit_t(&main_view, seekbar_height, &seekbar, &buttonbar);
|
||||
ui_hsplit_t(&buttonbar, margins, 0, &buttonbar);
|
||||
}
|
||||
else
|
||||
seekbar = main_view;
|
||||
|
||||
// do seekbar
|
||||
{
|
||||
static int seekbar_id = 0;
|
||||
void *id = &seekbar_id;
|
||||
char buffer[128];
|
||||
|
||||
ui_draw_rect(&seekbar, vec4(0,0,0,0.5f), CORNER_ALL, 5.0f);
|
||||
|
||||
int current_tick = info->current_tick - info->first_tick;
|
||||
int total_ticks = info->last_tick - info->first_tick;
|
||||
|
||||
float amount = current_tick/(float)total_ticks;
|
||||
|
||||
RECT filledbar = seekbar;
|
||||
filledbar.w = 10.0f + (filledbar.w-10.0f)*amount;
|
||||
|
||||
ui_draw_rect(&filledbar, vec4(1,1,1,0.5f), CORNER_ALL, 5.0f);
|
||||
|
||||
str_format(buffer, sizeof(buffer), "%d:%02d / %d:%02d",
|
||||
current_tick/SERVER_TICK_SPEED/60, (current_tick/SERVER_TICK_SPEED)%60,
|
||||
total_ticks/SERVER_TICK_SPEED/60, (total_ticks/SERVER_TICK_SPEED)%60);
|
||||
ui_do_label(&seekbar, buffer, seekbar.h*0.70f, 0);
|
||||
|
||||
// do the logic
|
||||
int inside = ui_mouse_inside(&seekbar);
|
||||
|
||||
if(ui_active_item() == id)
|
||||
{
|
||||
if(!ui_mouse_button(0))
|
||||
ui_set_active_item(0);
|
||||
else
|
||||
{
|
||||
float amount = (ui_mouse_x()-seekbar.x)/(float)seekbar.w;
|
||||
if(amount > 0 && amount < 1.0f)
|
||||
{
|
||||
gameclient.on_reset();
|
||||
gameclient.suppress_events = true;
|
||||
client_demoplayer_setpos(amount);
|
||||
gameclient.suppress_events = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ui_hot_item() == id)
|
||||
{
|
||||
if(ui_mouse_button(0))
|
||||
ui_set_active_item(id);
|
||||
}
|
||||
|
||||
if(inside)
|
||||
ui_set_hot_item(id);
|
||||
}
|
||||
|
||||
|
||||
if(menu_active)
|
||||
{
|
||||
// do buttons
|
||||
RECT button;
|
||||
|
||||
// pause button
|
||||
ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar);
|
||||
static int pause_button = 0;
|
||||
if(ui_do_button(&pause_button, "| |", info->paused, &button, ui_draw_demoplayer_button, 0))
|
||||
client_demoplayer_setpause(!info->paused);
|
||||
|
||||
// play button
|
||||
ui_vsplit_l(&buttonbar, margins, 0, &buttonbar);
|
||||
ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar);
|
||||
static int play_button = 0;
|
||||
if(ui_do_button(&play_button, ">", !info->paused, &button, ui_draw_demoplayer_button, 0))
|
||||
{
|
||||
client_demoplayer_setpause(0);
|
||||
client_demoplayer_setspeed(1.0f);
|
||||
}
|
||||
|
||||
// slowdown
|
||||
ui_vsplit_l(&buttonbar, margins, 0, &buttonbar);
|
||||
ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar);
|
||||
static int slowdown_button = 0;
|
||||
if(ui_do_button(&slowdown_button, "<<", 0, &button, ui_draw_demoplayer_button, 0))
|
||||
{
|
||||
if(info->speed > 4.0f) client_demoplayer_setspeed(4.0f);
|
||||
else if(info->speed > 2.0f) client_demoplayer_setspeed(2.0f);
|
||||
else if(info->speed > 1.0f) client_demoplayer_setspeed(1.0f);
|
||||
else if(info->speed > 0.5f) client_demoplayer_setspeed(0.5f);
|
||||
else client_demoplayer_setspeed(0.05f);
|
||||
}
|
||||
|
||||
// fastforward
|
||||
ui_vsplit_l(&buttonbar, margins, 0, &buttonbar);
|
||||
ui_vsplit_l(&buttonbar, buttonbar_height, &button, &buttonbar);
|
||||
static int fastforward_button = 0;
|
||||
if(ui_do_button(&fastforward_button, ">>", 0, &button, ui_draw_demoplayer_button, 0))
|
||||
{
|
||||
if(info->speed < 0.5f) client_demoplayer_setspeed(0.5f);
|
||||
else if(info->speed < 1.0f) client_demoplayer_setspeed(1.0f);
|
||||
else if(info->speed < 2.0f) client_demoplayer_setspeed(2.0f);
|
||||
else if(info->speed < 4.0f) client_demoplayer_setspeed(4.0f);
|
||||
else client_demoplayer_setspeed(8.0f);
|
||||
}
|
||||
|
||||
// speed meter
|
||||
ui_vsplit_l(&buttonbar, margins*3, 0, &buttonbar);
|
||||
char buffer[64];
|
||||
if(info->speed >= 1.0f)
|
||||
str_format(buffer, sizeof(buffer), "x%.0f", info->speed);
|
||||
else
|
||||
str_format(buffer, sizeof(buffer), "x%.1f", info->speed);
|
||||
ui_do_label(&buttonbar, buffer, button.h*0.7f, -1);
|
||||
|
||||
// exit button
|
||||
ui_vsplit_r(&buttonbar, buttonbar_height*3, &buttonbar, &button);
|
||||
static int exit_button = 0;
|
||||
if(ui_do_button(&exit_button, "Exit", 0, &button, ui_draw_demoplayer_button, 0))
|
||||
client_disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
static RECT listbox_originalview;
|
||||
static RECT listbox_view;
|
||||
static float listbox_rowheight;
|
||||
static int listbox_itemindex;
|
||||
static int listbox_selected_index;
|
||||
static int listbox_new_selected;
|
||||
|
||||
void MENUS::ui_do_listbox_start(void *id, const RECT *rect, float row_height, const char *title, int num_items, int selected_index)
|
||||
{
|
||||
RECT scroll, row;
|
||||
RECT view = *rect;
|
||||
RECT header, footer;
|
||||
|
||||
// draw header
|
||||
ui_hsplit_t(&view, listheader_height, &header, &view);
|
||||
ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f);
|
||||
ui_do_label(&header, title, header.h*fontmod_height, 0);
|
||||
|
||||
// draw footers
|
||||
ui_hsplit_b(&view, listheader_height, &view, &footer);
|
||||
ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f);
|
||||
ui_vsplit_l(&footer, 10.0f, 0, &footer);
|
||||
|
||||
// background
|
||||
ui_draw_rect(&view, vec4(0,0,0,0.15f), 0, 0);
|
||||
|
||||
// prepare the scroll
|
||||
ui_vsplit_r(&view, 15, &view, &scroll);
|
||||
|
||||
// setup the variables
|
||||
listbox_originalview = view;
|
||||
listbox_selected_index = selected_index;
|
||||
listbox_new_selected = selected_index;
|
||||
listbox_itemindex = 0;
|
||||
listbox_rowheight = row_height;
|
||||
//int num_servers = client_serverbrowse_sorted_num();
|
||||
|
||||
|
||||
// do the scrollbar
|
||||
ui_hsplit_t(&view, listbox_rowheight, &row, 0);
|
||||
|
||||
int num = (int)(listbox_originalview.h/row.h);
|
||||
static float scrollvalue = 0;
|
||||
ui_hmargin(&scroll, 5.0f, &scroll);
|
||||
scrollvalue = ui_do_scrollbar_v(id, &scroll, scrollvalue);
|
||||
|
||||
int start = (int)(num*scrollvalue);
|
||||
if(start < 0)
|
||||
start = 0;
|
||||
|
||||
// the list
|
||||
listbox_view = listbox_originalview;
|
||||
ui_vmargin(&listbox_view, 5.0f, &listbox_view);
|
||||
ui_clip_enable(&listbox_view);
|
||||
listbox_view.y -= scrollvalue*num*row.h;
|
||||
}
|
||||
|
||||
MENUS::LISTBOXITEM MENUS::ui_do_listbox_nextitem(void *id)
|
||||
{
|
||||
RECT row;
|
||||
LISTBOXITEM item = {0};
|
||||
ui_hsplit_t(&listbox_view, listbox_rowheight-2.0f, &row, &listbox_view);
|
||||
ui_hsplit_t(&listbox_view, 2.0f, 0, &listbox_view);
|
||||
|
||||
RECT select_hit_box = row;
|
||||
|
||||
item.visible = 1;
|
||||
if(listbox_selected_index == listbox_itemindex)
|
||||
item.selected = 1;
|
||||
|
||||
// make sure that only those in view can be selected
|
||||
if(row.y+row.h > listbox_originalview.y)
|
||||
{
|
||||
|
||||
if(select_hit_box.y < listbox_originalview.y) // clip the selection
|
||||
{
|
||||
select_hit_box.h -= listbox_originalview.y-select_hit_box.y;
|
||||
select_hit_box.y = listbox_originalview.y;
|
||||
}
|
||||
|
||||
if(ui_do_button(id, "", listbox_selected_index==listbox_itemindex, &select_hit_box, 0, 0))
|
||||
listbox_new_selected = listbox_itemindex;
|
||||
}
|
||||
else
|
||||
item.visible = 0;
|
||||
|
||||
item.rect = row;
|
||||
|
||||
// check if we need to do more
|
||||
if(row.y > listbox_originalview.y+listbox_originalview.h)
|
||||
item.visible = 0;
|
||||
|
||||
if(listbox_selected_index==listbox_itemindex)
|
||||
{
|
||||
//selected_index = i;
|
||||
RECT r = row;
|
||||
ui_margin(&r, 1.5f, &r);
|
||||
ui_draw_rect(&r, vec4(1,1,1,0.5f), CORNER_ALL, 4.0f);
|
||||
}
|
||||
|
||||
listbox_itemindex++;
|
||||
|
||||
ui_vmargin(&item.rect, 5.0f, &item.rect);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
int MENUS::ui_do_listbox_end()
|
||||
{
|
||||
ui_clip_disable();
|
||||
return listbox_new_selected;
|
||||
}
|
||||
|
||||
/*
|
||||
void MENUS::demolist_listdir_callback(const char *name, int is_dir, void *user)
|
||||
{
|
||||
|
||||
(*count)++;
|
||||
LISTBOXITEM item = ui_do_listbox_nextitem((void*)(10+*count));
|
||||
if(item.visible)
|
||||
ui_do_label(&item.rect, name, item.rect.h*fontmod_height, -1);
|
||||
}
|
||||
|
||||
|
||||
DEMOITEM *demos;
|
||||
int num_demos;
|
||||
*/
|
||||
|
||||
void MENUS::demolist_count_callback(const char *name, int is_dir, void *user)
|
||||
{
|
||||
if(is_dir || name[0] == '.')
|
||||
return;
|
||||
(*(int *)user)++;
|
||||
}
|
||||
|
||||
struct FETCH_CALLBACKINFO
|
||||
{
|
||||
MENUS *self;
|
||||
const char *prefix;
|
||||
int count;
|
||||
};
|
||||
|
||||
void MENUS::demolist_fetch_callback(const char *name, int is_dir, void *user)
|
||||
{
|
||||
if(is_dir || name[0] == '.')
|
||||
return;
|
||||
|
||||
FETCH_CALLBACKINFO *info = (FETCH_CALLBACKINFO *)user;
|
||||
|
||||
if(info->count == info->self->num_demos)
|
||||
return;
|
||||
|
||||
str_format(info->self->demos[info->count].filename, sizeof(info->self->demos[info->count].filename), "%s/%s", info->prefix, name);
|
||||
str_copy(info->self->demos[info->count].name, name, sizeof(info->self->demos[info->count].name));
|
||||
info->count++;
|
||||
}
|
||||
|
||||
|
||||
void MENUS::demolist_populate()
|
||||
{
|
||||
if(demos)
|
||||
mem_free(demos);
|
||||
demos = 0;
|
||||
num_demos = 0;
|
||||
|
||||
char buf[512];
|
||||
str_format(buf, sizeof(buf), "%s/demos", client_user_directory());
|
||||
fs_listdir(buf, demolist_count_callback, &num_demos);
|
||||
fs_listdir("demos", demolist_count_callback, &num_demos);
|
||||
|
||||
demos = (DEMOITEM *)mem_alloc(sizeof(DEMOITEM)*num_demos, 1);
|
||||
mem_zero(demos, sizeof(DEMOITEM)*num_demos);
|
||||
|
||||
FETCH_CALLBACKINFO info = {this, buf, 0};
|
||||
fs_listdir(buf, demolist_fetch_callback, &info);
|
||||
info.prefix = "demos";
|
||||
fs_listdir("demos", demolist_fetch_callback, &info);
|
||||
}
|
||||
|
||||
|
||||
void MENUS::render_demolist(RECT main_view)
|
||||
{
|
||||
static int inited = 0;
|
||||
if(!inited)
|
||||
demolist_populate();
|
||||
inited = 1;
|
||||
|
||||
// render background
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f);
|
||||
ui_margin(&main_view, 10.0f, &main_view);
|
||||
|
||||
RECT buttonbar;
|
||||
ui_hsplit_b(&main_view, button_height+5.0f, &main_view, &buttonbar);
|
||||
ui_hsplit_t(&buttonbar, 5.0f, 0, &buttonbar);
|
||||
|
||||
static int selected_item = -1;
|
||||
static int num_items = 0;
|
||||
static int demolist_id = 0;
|
||||
|
||||
ui_do_listbox_start(&demolist_id, &main_view, 17.0f, "Demos", num_items, selected_item);
|
||||
for(int i = 0; i < num_demos; i++)
|
||||
{
|
||||
LISTBOXITEM item = ui_do_listbox_nextitem((void*)(10+i));
|
||||
if(item.visible)
|
||||
ui_do_label(&item.rect, demos[i].name, item.rect.h*fontmod_height, -1);
|
||||
}
|
||||
selected_item = ui_do_listbox_end();
|
||||
|
||||
if(selected_item >= 0 && selected_item < num_demos && inp_mouse_doubleclick())
|
||||
{
|
||||
ui_set_active_item(0);
|
||||
client_demoplayer_play(demos[selected_item].filename);
|
||||
}
|
||||
|
||||
RECT refresh_rect, play_rect;
|
||||
ui_vsplit_r(&buttonbar, 250.0f, &buttonbar, &refresh_rect);
|
||||
ui_vsplit_r(&refresh_rect, 130.0f, &refresh_rect, &play_rect);
|
||||
ui_vsplit_r(&play_rect, 120.0f, 0x0, &play_rect);
|
||||
|
||||
static int refresh_button = 0;
|
||||
if(ui_do_button(&refresh_button, "Refresh", 0, &refresh_rect, ui_draw_menu_button, 0))
|
||||
{
|
||||
demolist_populate();
|
||||
}
|
||||
|
||||
static int play_button = 0;
|
||||
if(ui_do_button(&play_button, "Play", 0, &play_rect, ui_draw_menu_button, 0))
|
||||
{
|
||||
if(selected_item >= 0 && selected_item < num_demos)
|
||||
client_demoplayer_play(demos[selected_item].filename);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <string.h> // strcmp, strlen, strncpy
|
||||
#include <stdlib.h> // atoi
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
|
||||
#include "menus.hpp"
|
||||
#include "motd.hpp"
|
||||
#include "voting.hpp"
|
||||
|
||||
void MENUS::render_game(RECT main_view)
|
||||
{
|
||||
RECT button;
|
||||
//RECT votearea;
|
||||
ui_hsplit_t(&main_view, 45.0f, &main_view, 0);
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f);
|
||||
|
||||
ui_hsplit_t(&main_view, 10.0f, 0, &main_view);
|
||||
ui_hsplit_t(&main_view, 25.0f, &main_view, 0);
|
||||
ui_vmargin(&main_view, 10.0f, &main_view);
|
||||
|
||||
ui_vsplit_r(&main_view, 120.0f, &main_view, &button);
|
||||
static int disconnect_button = 0;
|
||||
if(ui_do_button(&disconnect_button, "Disconnect", 0, &button, ui_draw_menu_button, 0))
|
||||
client_disconnect();
|
||||
|
||||
if(gameclient.snap.local_info && gameclient.snap.gameobj)
|
||||
{
|
||||
if(gameclient.snap.local_info->team != -1)
|
||||
{
|
||||
ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
|
||||
ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
|
||||
static int spectate_button = 0;
|
||||
if(ui_do_button(&spectate_button, "Spectate", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
gameclient.send_switch_team(-1);
|
||||
set_active(false);
|
||||
}
|
||||
}
|
||||
|
||||
if(gameclient.snap.gameobj->flags & GAMEFLAG_TEAMS)
|
||||
{
|
||||
if(gameclient.snap.local_info->team != 0)
|
||||
{
|
||||
ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
|
||||
ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
|
||||
static int spectate_button = 0;
|
||||
if(ui_do_button(&spectate_button, "Join Red", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
gameclient.send_switch_team(0);
|
||||
set_active(false);
|
||||
}
|
||||
}
|
||||
|
||||
if(gameclient.snap.local_info->team != 1)
|
||||
{
|
||||
ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
|
||||
ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
|
||||
static int spectate_button = 0;
|
||||
if(ui_do_button(&spectate_button, "Join Blue", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
gameclient.send_switch_team(1);
|
||||
set_active(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(gameclient.snap.local_info->team != 0)
|
||||
{
|
||||
ui_vsplit_l(&main_view, 10.0f, &button, &main_view);
|
||||
ui_vsplit_l(&main_view, 120.0f, &button, &main_view);
|
||||
static int spectate_button = 0;
|
||||
if(ui_do_button(&spectate_button, "Join Game", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
gameclient.send_switch_team(0);
|
||||
set_active(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
RECT bars;
|
||||
ui_hsplit_t(&votearea, 10.0f, 0, &votearea);
|
||||
ui_hsplit_t(&votearea, 25.0f + 10.0f*3 + 25.0f, &votearea, &bars);
|
||||
|
||||
ui_draw_rect(&votearea, color_tabbar_active, CORNER_ALL, 10.0f);
|
||||
|
||||
ui_vmargin(&votearea, 20.0f, &votearea);
|
||||
ui_hmargin(&votearea, 10.0f, &votearea);
|
||||
|
||||
ui_hsplit_b(&votearea, 35.0f, &votearea, &bars);
|
||||
|
||||
if(gameclient.voting->is_voting())
|
||||
{
|
||||
// do yes button
|
||||
ui_vsplit_l(&votearea, 50.0f, &button, &votearea);
|
||||
static int yes_button = 0;
|
||||
if(ui_do_button(&yes_button, "Yes", 0, &button, ui_draw_menu_button, 0))
|
||||
gameclient.voting->vote(1);
|
||||
|
||||
// do no button
|
||||
ui_vsplit_l(&votearea, 5.0f, 0, &votearea);
|
||||
ui_vsplit_l(&votearea, 50.0f, &button, &votearea);
|
||||
static int no_button = 0;
|
||||
if(ui_do_button(&no_button, "No", 0, &button, ui_draw_menu_button, 0))
|
||||
gameclient.voting->vote(-1);
|
||||
|
||||
// do time left
|
||||
ui_vsplit_r(&votearea, 50.0f, &votearea, &button);
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "%d", gameclient.voting->seconds_left());
|
||||
ui_do_label(&button, buf, 24.0f, 0);
|
||||
|
||||
// do description and command
|
||||
ui_vsplit_l(&votearea, 5.0f, 0, &votearea);
|
||||
ui_do_label(&votearea, gameclient.voting->vote_description(), 14.0f, -1);
|
||||
ui_hsplit_t(&votearea, 16.0f, 0, &votearea);
|
||||
ui_do_label(&votearea, gameclient.voting->vote_command(), 10.0f, -1);
|
||||
|
||||
// do bars
|
||||
ui_hsplit_t(&bars, 10.0f, 0, &bars);
|
||||
ui_hmargin(&bars, 5.0f, &bars);
|
||||
|
||||
gameclient.voting->render_bars(bars, true);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
ui_do_label(&votearea, "No vote in progress", 18.0f, -1);
|
||||
}*/
|
||||
}
|
||||
|
||||
void MENUS::render_serverinfo(RECT main_view)
|
||||
{
|
||||
// fetch server info
|
||||
SERVER_INFO current_server_info;
|
||||
client_serverinfo(¤t_server_info);
|
||||
|
||||
if(!gameclient.snap.local_info)
|
||||
return;
|
||||
|
||||
// count players for server info-box
|
||||
int num_players = 0;
|
||||
for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
snap_get_item(SNAP_CURRENT, i, &item);
|
||||
|
||||
if(item.type == NETOBJTYPE_PLAYER_INFO)
|
||||
{
|
||||
num_players++;
|
||||
}
|
||||
}
|
||||
|
||||
// render background
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_ALL, 10.0f);
|
||||
|
||||
RECT view, serverinfo, gameinfo, motd;
|
||||
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
char buf[1024];
|
||||
|
||||
// set view to use for all sub-modules
|
||||
ui_margin(&main_view, 10.0f, &view);
|
||||
|
||||
/* serverinfo */
|
||||
ui_hsplit_t(&view, view.h/2-5.0f, &serverinfo, &motd);
|
||||
ui_vsplit_l(&serverinfo, view.w/2-5.0f, &serverinfo, &gameinfo);
|
||||
ui_draw_rect(&serverinfo, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
|
||||
ui_margin(&serverinfo, 5.0f, &serverinfo);
|
||||
|
||||
x = 5.0f;
|
||||
y = 0.0f;
|
||||
|
||||
gfx_text(0, serverinfo.x+x, serverinfo.y+y, 32, "Server info", 250);
|
||||
y += 32.0f+5.0f;
|
||||
|
||||
mem_zero(buf, sizeof(buf));
|
||||
str_format(
|
||||
buf,
|
||||
sizeof(buf),
|
||||
"%s\n\n"
|
||||
"Address: %s\n"
|
||||
"Ping: %d\n"
|
||||
"Version: %s\n"
|
||||
"Password: %s\n",
|
||||
current_server_info.name,
|
||||
config.ui_server_address,
|
||||
gameclient.snap.local_info->latency,
|
||||
current_server_info.version,
|
||||
current_server_info.flags&1 ? "Yes" : "No"
|
||||
);
|
||||
|
||||
gfx_text(0, serverinfo.x+x, serverinfo.y+y, 20, buf, 250);
|
||||
|
||||
{
|
||||
RECT button;
|
||||
int is_favorite = client_serverbrowse_isfavorite(current_server_info.netaddr);
|
||||
ui_hsplit_b(&serverinfo, 20.0f, &serverinfo, &button);
|
||||
static int add_fav_button = 0;
|
||||
if (ui_do_button(&add_fav_button, "Favorite", is_favorite, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
if(is_favorite)
|
||||
client_serverbrowse_removefavorite(current_server_info.netaddr);
|
||||
else
|
||||
client_serverbrowse_addfavorite(current_server_info.netaddr);
|
||||
}
|
||||
}
|
||||
|
||||
/* gameinfo */
|
||||
ui_vsplit_l(&gameinfo, 10.0f, 0x0, &gameinfo);
|
||||
ui_draw_rect(&gameinfo, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
|
||||
ui_margin(&gameinfo, 5.0f, &gameinfo);
|
||||
|
||||
x = 5.0f;
|
||||
y = 0.0f;
|
||||
|
||||
gfx_text(0, gameinfo.x+x, gameinfo.y+y, 32, "Game info", 250);
|
||||
y += 32.0f+5.0f;
|
||||
|
||||
mem_zero(buf, sizeof(buf));
|
||||
str_format(
|
||||
buf,
|
||||
sizeof(buf),
|
||||
"\n\n"
|
||||
"Gametype: %s\n"
|
||||
"Map: %s\n"
|
||||
"Score limit: %d\n"
|
||||
"Time limit: %d\n"
|
||||
"\n"
|
||||
"Players: %d/%d\n",
|
||||
current_server_info.gametype,
|
||||
current_server_info.map,
|
||||
gameclient.snap.gameobj->score_limit,
|
||||
gameclient.snap.gameobj->time_limit,
|
||||
gameclient.snap.num_players,
|
||||
current_server_info.max_players
|
||||
);
|
||||
gfx_text(0, gameinfo.x+x, gameinfo.y+y, 20, buf, 250);
|
||||
|
||||
/* motd */
|
||||
ui_hsplit_t(&motd, 10.0f, 0, &motd);
|
||||
ui_draw_rect(&motd, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
ui_margin(&motd, 5.0f, &motd);
|
||||
y = 0.0f;
|
||||
x = 5.0f;
|
||||
gfx_text(0, motd.x+x, motd.y+y, 32, "MOTD", -1);
|
||||
y += 32.0f+5.0f;
|
||||
gfx_text(0, motd.x+x, motd.y+y, 16, gameclient.motd->server_motd, (int)motd.w);
|
||||
}
|
||||
|
||||
static const char *format_command(const char *cmd)
|
||||
{
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void MENUS::render_servercontrol_server(RECT main_view)
|
||||
{
|
||||
int num_options = 0;
|
||||
for(VOTING::VOTEOPTION *option = gameclient.voting->first; option; option = option->next)
|
||||
num_options++;
|
||||
|
||||
static int votelist = 0;
|
||||
RECT list = main_view;
|
||||
ui_do_listbox_start(&votelist, &list, 24.0f, "Options", num_options, callvote_selectedoption);
|
||||
|
||||
for(VOTING::VOTEOPTION *option = gameclient.voting->first; option; option = option->next)
|
||||
{
|
||||
LISTBOXITEM item = ui_do_listbox_nextitem(option);
|
||||
|
||||
if(item.visible)
|
||||
ui_do_label(&item.rect, format_command(option->command), 16.0f, -1);
|
||||
}
|
||||
|
||||
callvote_selectedoption = ui_do_listbox_end();
|
||||
}
|
||||
|
||||
void MENUS::render_servercontrol_kick(RECT main_view)
|
||||
{
|
||||
// draw header
|
||||
RECT header, footer;
|
||||
ui_hsplit_t(&main_view, 20, &header, &main_view);
|
||||
ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f);
|
||||
ui_do_label(&header, "Players", 18.0f, 0);
|
||||
|
||||
// draw footers
|
||||
ui_hsplit_b(&main_view, 20, &main_view, &footer);
|
||||
ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f);
|
||||
ui_vsplit_l(&footer, 10.0f, 0, &footer);
|
||||
|
||||
// players
|
||||
ui_draw_rect(&main_view, vec4(0,0,0,0.15f), 0, 0);
|
||||
RECT list = main_view;
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(!gameclient.snap.player_infos[i])
|
||||
continue;
|
||||
|
||||
RECT button;
|
||||
ui_hsplit_t(&list, button_height, &button, &list);
|
||||
|
||||
if(ui_do_button((char *)&gameclient.snap+i, "", callvote_selectedplayer == i, &button, ui_draw_list_row, 0))
|
||||
callvote_selectedplayer = i;
|
||||
|
||||
TEE_RENDER_INFO info = gameclient.clients[i].render_info;
|
||||
info.size = button.h;
|
||||
render_tee(ANIMSTATE::get_idle(), &info, EMOTE_NORMAL, vec2(1,0), vec2(button.x+button.h/2, button.y+button.h/2));
|
||||
|
||||
button.x += button.h;
|
||||
ui_do_label(&button, gameclient.clients[i].name, 18.0f, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void MENUS::render_servercontrol(RECT main_view)
|
||||
{
|
||||
static int control_page = 0;
|
||||
|
||||
// render background
|
||||
RECT temp, tabbar;
|
||||
ui_vsplit_r(&main_view, 120.0f, &main_view, &tabbar);
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f);
|
||||
ui_hsplit_t(&tabbar, 50.0f, &temp, &tabbar);
|
||||
ui_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f);
|
||||
|
||||
ui_hsplit_t(&main_view, 10.0f, 0, &main_view);
|
||||
|
||||
RECT button;
|
||||
|
||||
const char *tabs[] = {"Options", "Kick"};
|
||||
int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs));
|
||||
|
||||
for(int i = 0; i < num_tabs; i++)
|
||||
{
|
||||
ui_hsplit_t(&tabbar, 10, &button, &tabbar);
|
||||
ui_hsplit_t(&tabbar, 26, &button, &tabbar);
|
||||
if(ui_do_button(tabs[i], tabs[i], control_page == i, &button, ui_draw_settings_tab_button, 0))
|
||||
{
|
||||
control_page = i;
|
||||
callvote_selectedplayer = -1;
|
||||
callvote_selectedoption = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ui_margin(&main_view, 10.0f, &main_view);
|
||||
RECT bottom;
|
||||
ui_hsplit_b(&main_view, button_height + 5*2, &main_view, &bottom);
|
||||
ui_hmargin(&bottom, 5.0f, &bottom);
|
||||
|
||||
// render page
|
||||
if(control_page == 0)
|
||||
render_servercontrol_server(main_view);
|
||||
else if(control_page == 1)
|
||||
render_servercontrol_kick(main_view);
|
||||
|
||||
|
||||
{
|
||||
RECT button;
|
||||
ui_vsplit_r(&bottom, 120.0f, &bottom, &button);
|
||||
|
||||
static int callvote_button = 0;
|
||||
if(ui_do_button(&callvote_button, "Call vote", 0, &button, ui_draw_menu_button, 0))
|
||||
{
|
||||
if(control_page == 0)
|
||||
{
|
||||
//
|
||||
gameclient.voting->callvote_option(callvote_selectedoption);
|
||||
/*
|
||||
if(callvote_selectedmap >= 0 && callvote_selectedmap < gameclient.maplist->num())
|
||||
gameclient.voting->callvote_map(gameclient.maplist->name(callvote_selectedmap));*/
|
||||
}
|
||||
else if(control_page == 1)
|
||||
{
|
||||
if(callvote_selectedplayer >= 0 && callvote_selectedplayer < MAX_CLIENTS &&
|
||||
gameclient.snap.player_infos[callvote_selectedplayer])
|
||||
{
|
||||
gameclient.voting->callvote_kick(callvote_selectedplayer);
|
||||
set_active(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,667 @@
|
||||
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <string.h> // strcmp, strlen, strncpy
|
||||
#include <stdlib.h> // atoi
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
|
||||
#include "binds.hpp"
|
||||
#include "menus.hpp"
|
||||
#include "skins.hpp"
|
||||
|
||||
MENUS_KEYBINDER MENUS::binder;
|
||||
|
||||
MENUS_KEYBINDER::MENUS_KEYBINDER()
|
||||
{
|
||||
take_key = false;
|
||||
got_key = false;
|
||||
}
|
||||
|
||||
bool MENUS_KEYBINDER::on_input(INPUT_EVENT e)
|
||||
{
|
||||
if(take_key)
|
||||
{
|
||||
if(e.flags&INPFLAG_PRESS && e.key != KEY_ESCAPE)
|
||||
{
|
||||
key = e;
|
||||
got_key = true;
|
||||
take_key = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MENUS::render_settings_player(RECT main_view)
|
||||
{
|
||||
RECT button;
|
||||
RECT skinselection;
|
||||
ui_vsplit_l(&main_view, 300.0f, &main_view, &skinselection);
|
||||
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
|
||||
// render settings
|
||||
{
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
ui_do_label(&button, "Name:", 14.0, -1);
|
||||
ui_vsplit_l(&button, 80.0f, 0, &button);
|
||||
ui_vsplit_l(&button, 180.0f, &button, 0);
|
||||
if(ui_do_edit_box(config.player_name, &button, config.player_name, sizeof(config.player_name), 14.0f))
|
||||
need_sendinfo = true;
|
||||
|
||||
static int dynamic_camera_button = 0;
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if(ui_do_button(&dynamic_camera_button, "Dynamic Camera", config.cl_mouse_deadzone != 0, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
|
||||
if(config.cl_mouse_deadzone)
|
||||
{
|
||||
config.cl_mouse_followfactor = 0;
|
||||
config.cl_mouse_max_distance = 400;
|
||||
config.cl_mouse_deadzone = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.cl_mouse_followfactor = 60;
|
||||
config.cl_mouse_max_distance = 1000;
|
||||
config.cl_mouse_deadzone = 300;
|
||||
}
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.cl_autoswitch_weapons, "Switch weapon on pickup", config.cl_autoswitch_weapons, &button, ui_draw_checkbox, 0))
|
||||
config.cl_autoswitch_weapons ^= 1;
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.cl_nameplates, "Show name plates", config.cl_nameplates, &button, ui_draw_checkbox, 0))
|
||||
config.cl_nameplates ^= 1;
|
||||
|
||||
//if(config.cl_nameplates)
|
||||
{
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
ui_vsplit_l(&button, 15.0f, 0, &button);
|
||||
if (ui_do_button(&config.cl_nameplates_always, "Always show name plates", config.cl_nameplates_always, &button, ui_draw_checkbox, 0))
|
||||
config.cl_nameplates_always ^= 1;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.player_color_body, "Custom colors", config.player_use_custom_color, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.player_use_custom_color = config.player_use_custom_color?0:1;
|
||||
need_sendinfo = true;
|
||||
}
|
||||
|
||||
if(config.player_use_custom_color)
|
||||
{
|
||||
int *colors[2];
|
||||
colors[0] = &config.player_color_body;
|
||||
colors[1] = &config.player_color_feet;
|
||||
|
||||
const char *parts[] = {"Body", "Feet"};
|
||||
const char *labels[] = {"Hue", "Sat.", "Lht."};
|
||||
static int color_slider[2][3] = {{0}};
|
||||
//static float v[2][3] = {{0, 0.5f, 0.25f}, {0, 0.5f, 0.25f}};
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
RECT text;
|
||||
ui_hsplit_t(&main_view, 20.0f, &text, &main_view);
|
||||
ui_vsplit_l(&text, 15.0f, 0, &text);
|
||||
ui_do_label(&text, parts[i], 14.0f, -1);
|
||||
|
||||
int prevcolor = *colors[i];
|
||||
int color = 0;
|
||||
for(int s = 0; s < 3; s++)
|
||||
{
|
||||
RECT text;
|
||||
ui_hsplit_t(&main_view, 19.0f, &button, &main_view);
|
||||
ui_vsplit_l(&button, 30.0f, 0, &button);
|
||||
ui_vsplit_l(&button, 30.0f, &text, &button);
|
||||
ui_vsplit_r(&button, 5.0f, &button, 0);
|
||||
ui_hsplit_t(&button, 4.0f, 0, &button);
|
||||
|
||||
float k = ((prevcolor>>((2-s)*8))&0xff) / 255.0f;
|
||||
k = ui_do_scrollbar_h(&color_slider[i][s], &button, k);
|
||||
color <<= 8;
|
||||
color += clamp((int)(k*255), 0, 255);
|
||||
ui_do_label(&text, labels[s], 15.0f, -1);
|
||||
|
||||
}
|
||||
|
||||
if(*colors[i] != color)
|
||||
need_sendinfo = true;
|
||||
|
||||
*colors[i] = color;
|
||||
ui_hsplit_t(&main_view, 5.0f, 0, &main_view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// draw header
|
||||
RECT header, footer;
|
||||
ui_hsplit_t(&skinselection, 20, &header, &skinselection);
|
||||
ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f);
|
||||
ui_do_label(&header, "Skins", 18.0f, 0);
|
||||
|
||||
// draw footers
|
||||
ui_hsplit_b(&skinselection, 20, &skinselection, &footer);
|
||||
ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f);
|
||||
ui_vsplit_l(&footer, 10.0f, 0, &footer);
|
||||
|
||||
// modes
|
||||
ui_draw_rect(&skinselection, vec4(0,0,0,0.15f), 0, 0);
|
||||
|
||||
RECT scroll;
|
||||
ui_vsplit_r(&skinselection, 15, &skinselection, &scroll);
|
||||
|
||||
RECT list = skinselection;
|
||||
ui_hsplit_t(&list, 50, &button, &list);
|
||||
|
||||
int num = (int)(skinselection.h/button.h);
|
||||
static float scrollvalue = 0;
|
||||
static int scrollbar = 0;
|
||||
ui_hmargin(&scroll, 5.0f, &scroll);
|
||||
scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue);
|
||||
|
||||
int start = (int)((gameclient.skins->num()-num)*scrollvalue);
|
||||
if(start < 0)
|
||||
start = 0;
|
||||
|
||||
for(int i = start; i < start+num && i < gameclient.skins->num(); i++)
|
||||
{
|
||||
const SKINS::SKIN *s = gameclient.skins->get(i);
|
||||
|
||||
// no special skins
|
||||
if(s->name[0] == 'x' && s->name[1] == '_')
|
||||
{
|
||||
num++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
str_format(buf, sizeof(buf), "%s", s->name);
|
||||
int selected = 0;
|
||||
if(strcmp(s->name, config.player_skin) == 0)
|
||||
selected = 1;
|
||||
|
||||
TEE_RENDER_INFO info;
|
||||
info.texture = s->org_texture;
|
||||
info.color_body = vec4(1,1,1,1);
|
||||
info.color_feet = vec4(1,1,1,1);
|
||||
if(config.player_use_custom_color)
|
||||
{
|
||||
info.color_body = gameclient.skins->get_color(config.player_color_body);
|
||||
info.color_feet = gameclient.skins->get_color(config.player_color_feet);
|
||||
info.texture = s->color_texture;
|
||||
}
|
||||
|
||||
info.size = ui_scale()*50.0f;
|
||||
|
||||
RECT icon;
|
||||
RECT text;
|
||||
ui_vsplit_l(&button, 50.0f, &icon, &text);
|
||||
|
||||
if(ui_do_button(s, "", selected, &button, ui_draw_list_row, 0))
|
||||
{
|
||||
config_set_player_skin(&config, s->name);
|
||||
need_sendinfo = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&text, 12.0f, 0, &text); // some margin from the top
|
||||
ui_do_label(&text, buf, 18.0f, 0);
|
||||
|
||||
ui_hsplit_t(&icon, 5.0f, 0, &icon); // some margin from the top
|
||||
render_tee(ANIMSTATE::get_idle(), &info, 0, vec2(1, 0), vec2(icon.x+icon.w/2, icon.y+icon.h/2));
|
||||
|
||||
if(config.debug)
|
||||
{
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(s->blood_color.r, s->blood_color.g, s->blood_color.b, 1.0f);
|
||||
gfx_quads_drawTL(icon.x, icon.y, 12, 12);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
ui_hsplit_t(&list, 50, &button, &list);
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*assign_func_callback)(CONFIGURATION *config, int value);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
const char *command;
|
||||
int keyid;
|
||||
} KEYINFO;
|
||||
|
||||
KEYINFO keys[] =
|
||||
{
|
||||
{ "Move Left:", "+left", 0},
|
||||
{ "Move Right:", "+right", 0 },
|
||||
{ "Jump:", "+jump", 0 },
|
||||
{ "Fire:", "+fire", 0 },
|
||||
{ "Hook:", "+hook", 0 },
|
||||
{ "Hammer:", "+weapon1", 0 },
|
||||
{ "Pistol:", "+weapon2", 0 },
|
||||
{ "Shotgun:", "+weapon3", 0 },
|
||||
{ "Grenade:", "+weapon4", 0 },
|
||||
{ "Rifle:", "+weapon5", 0 },
|
||||
{ "Next Weapon:", "+nextweapon", 0 },
|
||||
{ "Prev. Weapon:", "+prevweapon", 0 },
|
||||
{ "Vote Yes:", "vote yes", 0 },
|
||||
{ "Vote No:", "vote no", 0 },
|
||||
{ "Chat:", "chat all", 0 },
|
||||
{ "Team Chat:", "chat team", 0 },
|
||||
{ "Emoticon:", "+emote", 0 },
|
||||
{ "Console:", "toggle_local_console", 0 },
|
||||
{ "Remote Console:", "toggle_remote_console", 0 },
|
||||
{ "Screenshot:", "screenshot", 0 },
|
||||
{ "Scoreboard:", "+scoreboard", 0 },
|
||||
};
|
||||
|
||||
const int key_count = sizeof(keys) / sizeof(KEYINFO);
|
||||
|
||||
void MENUS::ui_do_getbuttons(int start, int stop, RECT view)
|
||||
{
|
||||
for (int i = start; i < stop; i++)
|
||||
{
|
||||
KEYINFO key = keys[i];
|
||||
RECT button, label;
|
||||
ui_hsplit_t(&view, 20.0f, &button, &view);
|
||||
ui_vsplit_l(&button, 130.0f, &label, &button);
|
||||
|
||||
ui_do_label(&label, key.name, 14.0f, -1);
|
||||
int oldid = key.keyid;
|
||||
int newid = ui_do_key_reader((void *)keys[i].name, &button, oldid);
|
||||
if(newid != oldid)
|
||||
{
|
||||
gameclient.binds->bind(oldid, "");
|
||||
gameclient.binds->bind(newid, keys[i].command);
|
||||
}
|
||||
ui_hsplit_t(&view, 5.0f, 0, &view);
|
||||
}
|
||||
}
|
||||
|
||||
void MENUS::render_settings_controls(RECT main_view)
|
||||
{
|
||||
// this is kinda slow, but whatever
|
||||
for(int i = 0; i < key_count; i++)
|
||||
keys[i].keyid = 0;
|
||||
|
||||
for(int keyid = 0; keyid < KEY_LAST; keyid++)
|
||||
{
|
||||
const char *bind = gameclient.binds->get(keyid);
|
||||
if(!bind[0])
|
||||
continue;
|
||||
|
||||
for(int i = 0; i < key_count; i++)
|
||||
if(strcmp(bind, keys[i].command) == 0)
|
||||
{
|
||||
keys[i].keyid = keyid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RECT movement_settings, weapon_settings, voting_settings, chat_settings, misc_settings, reset_button;
|
||||
ui_vsplit_l(&main_view, main_view.w/2-5.0f, &movement_settings, &voting_settings);
|
||||
|
||||
/* movement settings */
|
||||
{
|
||||
ui_hsplit_t(&movement_settings, main_view.h/2-5.0f, &movement_settings, &weapon_settings);
|
||||
ui_draw_rect(&movement_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
ui_margin(&movement_settings, 10.0f, &movement_settings);
|
||||
|
||||
gfx_text(0, movement_settings.x, movement_settings.y, 14, "Movement", -1);
|
||||
|
||||
ui_hsplit_t(&movement_settings, 14.0f+5.0f+10.0f, 0, &movement_settings);
|
||||
|
||||
{
|
||||
RECT button, label;
|
||||
ui_hsplit_t(&movement_settings, 20.0f, &button, &movement_settings);
|
||||
ui_vsplit_l(&button, 130.0f, &label, &button);
|
||||
ui_do_label(&label, "Mouse sens.", 14.0f, -1);
|
||||
ui_hmargin(&button, 2.0f, &button);
|
||||
config.inp_mousesens = (int)(ui_do_scrollbar_h(&config.inp_mousesens, &button, (config.inp_mousesens-5)/500.0f)*500.0f)+5;
|
||||
//*key.key = ui_do_key_reader(key.key, &button, *key.key);
|
||||
ui_hsplit_t(&movement_settings, 20.0f, 0, &movement_settings);
|
||||
}
|
||||
|
||||
ui_do_getbuttons(0, 5, movement_settings);
|
||||
|
||||
}
|
||||
|
||||
/* weapon settings */
|
||||
{
|
||||
ui_hsplit_t(&weapon_settings, 10.0f, 0, &weapon_settings);
|
||||
ui_draw_rect(&weapon_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
ui_margin(&weapon_settings, 10.0f, &weapon_settings);
|
||||
|
||||
gfx_text(0, weapon_settings.x, weapon_settings.y, 14, "Weapon", -1);
|
||||
|
||||
ui_hsplit_t(&weapon_settings, 14.0f+5.0f+10.0f, 0, &weapon_settings);
|
||||
ui_do_getbuttons(5, 12, weapon_settings);
|
||||
}
|
||||
|
||||
/* voting settings */
|
||||
{
|
||||
ui_vsplit_l(&voting_settings, 10.0f, 0, &voting_settings);
|
||||
ui_hsplit_t(&voting_settings, main_view.h/4-5.0f, &voting_settings, &chat_settings);
|
||||
ui_draw_rect(&voting_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
ui_margin(&voting_settings, 10.0f, &voting_settings);
|
||||
|
||||
gfx_text(0, voting_settings.x, voting_settings.y, 14, "Voting", -1);
|
||||
|
||||
ui_hsplit_t(&voting_settings, 14.0f+5.0f+10.0f, 0, &voting_settings);
|
||||
ui_do_getbuttons(12, 14, voting_settings);
|
||||
}
|
||||
|
||||
/* chat settings */
|
||||
{
|
||||
ui_hsplit_t(&chat_settings, 10.0f, 0, &chat_settings);
|
||||
ui_hsplit_t(&chat_settings, main_view.h/4-10.0f, &chat_settings, &misc_settings);
|
||||
ui_draw_rect(&chat_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
ui_margin(&chat_settings, 10.0f, &chat_settings);
|
||||
|
||||
gfx_text(0, chat_settings.x, chat_settings.y, 14, "Chat", -1);
|
||||
|
||||
ui_hsplit_t(&chat_settings, 14.0f+5.0f+10.0f, 0, &chat_settings);
|
||||
ui_do_getbuttons(14, 16, chat_settings);
|
||||
}
|
||||
|
||||
/* misc settings */
|
||||
{
|
||||
ui_hsplit_t(&misc_settings, 10.0f, 0, &misc_settings);
|
||||
ui_hsplit_t(&misc_settings, main_view.h/2-5.0f-45.0f, &misc_settings, &reset_button);
|
||||
ui_draw_rect(&misc_settings, vec4(1,1,1,0.25f), CORNER_ALL, 10.0f);
|
||||
ui_margin(&misc_settings, 10.0f, &misc_settings);
|
||||
|
||||
gfx_text(0, misc_settings.x, misc_settings.y, 14, "Miscellaneous", -1);
|
||||
|
||||
ui_hsplit_t(&misc_settings, 14.0f+5.0f+10.0f, 0, &misc_settings);
|
||||
ui_do_getbuttons(16, 21, misc_settings);
|
||||
}
|
||||
|
||||
// defaults
|
||||
ui_hsplit_t(&reset_button, 10.0f, 0, &reset_button);
|
||||
static int default_button = 0;
|
||||
if (ui_do_button((void*)&default_button, "Reset to defaults", 0, &reset_button, ui_draw_menu_button, 0))
|
||||
gameclient.binds->set_defaults();
|
||||
}
|
||||
|
||||
void MENUS::render_settings_graphics(RECT main_view)
|
||||
{
|
||||
RECT button;
|
||||
char buf[128];
|
||||
|
||||
static const int MAX_RESOLUTIONS = 256;
|
||||
static VIDEO_MODE modes[MAX_RESOLUTIONS];
|
||||
static int num_modes = -1;
|
||||
|
||||
if(num_modes == -1)
|
||||
num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS);
|
||||
|
||||
RECT modelist;
|
||||
ui_vsplit_l(&main_view, 300.0f, &main_view, &modelist);
|
||||
|
||||
// draw allmodes switch
|
||||
RECT header, footer;
|
||||
ui_hsplit_t(&modelist, 20, &button, &modelist);
|
||||
if (ui_do_button(&config.gfx_display_all_modes, "Show only supported", config.gfx_display_all_modes^1, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.gfx_display_all_modes ^= 1;
|
||||
num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS);
|
||||
}
|
||||
|
||||
// draw header
|
||||
ui_hsplit_t(&modelist, 20, &header, &modelist);
|
||||
ui_draw_rect(&header, vec4(1,1,1,0.25f), CORNER_T, 5.0f);
|
||||
ui_do_label(&header, "Display Modes", 14.0f, 0);
|
||||
|
||||
// draw footers
|
||||
ui_hsplit_b(&modelist, 20, &modelist, &footer);
|
||||
str_format(buf, sizeof(buf), "Current: %dx%d %d bit", config.gfx_screen_width, config.gfx_screen_height, config.gfx_color_depth);
|
||||
ui_draw_rect(&footer, vec4(1,1,1,0.25f), CORNER_B, 5.0f);
|
||||
ui_vsplit_l(&footer, 10.0f, 0, &footer);
|
||||
ui_do_label(&footer, buf, 14.0f, -1);
|
||||
|
||||
// modes
|
||||
ui_draw_rect(&modelist, vec4(0,0,0,0.15f), 0, 0);
|
||||
|
||||
RECT scroll;
|
||||
ui_vsplit_r(&modelist, 15, &modelist, &scroll);
|
||||
|
||||
RECT list = modelist;
|
||||
ui_hsplit_t(&list, 20, &button, &list);
|
||||
|
||||
int num = (int)(modelist.h/button.h);
|
||||
static float scrollvalue = 0;
|
||||
static int scrollbar = 0;
|
||||
ui_hmargin(&scroll, 5.0f, &scroll);
|
||||
scrollvalue = ui_do_scrollbar_v(&scrollbar, &scroll, scrollvalue);
|
||||
|
||||
int start = (int)((num_modes-num)*scrollvalue);
|
||||
if(start < 0)
|
||||
start = 0;
|
||||
|
||||
for(int i = start; i < start+num && i < num_modes; i++)
|
||||
{
|
||||
int depth = modes[i].red+modes[i].green+modes[i].blue;
|
||||
if(depth < 16)
|
||||
depth = 16;
|
||||
else if(depth > 16)
|
||||
depth = 24;
|
||||
|
||||
int selected = 0;
|
||||
if(config.gfx_color_depth == depth &&
|
||||
config.gfx_screen_width == modes[i].width &&
|
||||
config.gfx_screen_height == modes[i].height)
|
||||
{
|
||||
selected = 1;
|
||||
}
|
||||
|
||||
str_format(buf, sizeof(buf), " %dx%d %d bit", modes[i].width, modes[i].height, depth);
|
||||
if(ui_do_button(&modes[i], buf, selected, &button, ui_draw_list_row, 0))
|
||||
{
|
||||
config.gfx_color_depth = depth;
|
||||
config.gfx_screen_width = modes[i].width;
|
||||
config.gfx_screen_height = modes[i].height;
|
||||
if(!selected)
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&list, 20, &button, &list);
|
||||
}
|
||||
|
||||
|
||||
// switches
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.gfx_fullscreen, "Fullscreen", config.gfx_fullscreen, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.gfx_fullscreen ^= 1;
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.gfx_vsync, "V-Sync", config.gfx_vsync, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.gfx_vsync ^= 1;
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.gfx_fsaa_samples, "FSAA samples", config.gfx_fsaa_samples, &button, ui_draw_checkbox_number, 0))
|
||||
{
|
||||
config.gfx_fsaa_samples = (config.gfx_fsaa_samples+1)%17;
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 40.0f, &button, &main_view);
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.gfx_texture_quality, "Quality Textures", config.gfx_texture_quality, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.gfx_texture_quality ^= 1;
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.gfx_texture_compression, "Texture Compression", config.gfx_texture_compression, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.gfx_texture_compression ^= 1;
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.gfx_high_detail, "High Detail", config.gfx_high_detail, &button, ui_draw_checkbox, 0))
|
||||
config.gfx_high_detail ^= 1;
|
||||
|
||||
//
|
||||
|
||||
RECT text;
|
||||
ui_hsplit_t(&main_view, 20.0f, 0, &main_view);
|
||||
ui_hsplit_t(&main_view, 20.0f, &text, &main_view);
|
||||
//ui_vsplit_l(&text, 15.0f, 0, &text);
|
||||
ui_do_label(&text, "UI Color", 14.0f, -1);
|
||||
|
||||
const char *labels[] = {"Hue", "Sat.", "Lht.", "Alpha"};
|
||||
int *color_slider[4] = {&config.ui_color_hue, &config.ui_color_sat, &config.ui_color_lht, &config.ui_color_alpha};
|
||||
for(int s = 0; s < 4; s++)
|
||||
{
|
||||
RECT text;
|
||||
ui_hsplit_t(&main_view, 19.0f, &button, &main_view);
|
||||
ui_vmargin(&button, 15.0f, &button);
|
||||
ui_vsplit_l(&button, 50.0f, &text, &button);
|
||||
ui_vsplit_r(&button, 5.0f, &button, 0);
|
||||
ui_hsplit_t(&button, 4.0f, 0, &button);
|
||||
|
||||
float k = (*color_slider[s]) / 255.0f;
|
||||
k = ui_do_scrollbar_h(color_slider[s], &button, k);
|
||||
*color_slider[s] = (int)(k*255.0f);
|
||||
ui_do_label(&text, labels[s], 15.0f, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void MENUS::render_settings_sound(RECT main_view)
|
||||
{
|
||||
RECT button;
|
||||
ui_vsplit_l(&main_view, 300.0f, &main_view, 0);
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.snd_enable, "Use Sounds", config.snd_enable, &button, ui_draw_checkbox, 0))
|
||||
{
|
||||
config.snd_enable ^= 1;
|
||||
need_restart = true;
|
||||
}
|
||||
|
||||
if(!config.snd_enable)
|
||||
return;
|
||||
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
if (ui_do_button(&config.snd_nonactive_mute, "Mute when not active", config.snd_nonactive_mute, &button, ui_draw_checkbox, 0))
|
||||
config.snd_nonactive_mute ^= 1;
|
||||
|
||||
// sample rate box
|
||||
{
|
||||
char buf[64];
|
||||
str_format(buf, sizeof(buf), "%d", config.snd_rate);
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
ui_do_label(&button, "Sample Rate", 14.0f, -1);
|
||||
ui_vsplit_l(&button, 110.0f, 0, &button);
|
||||
ui_vsplit_l(&button, 180.0f, &button, 0);
|
||||
ui_do_edit_box(&config.snd_rate, &button, buf, sizeof(buf), 14.0f);
|
||||
int before = config.snd_rate;
|
||||
config.snd_rate = atoi(buf);
|
||||
|
||||
if(config.snd_rate != before)
|
||||
need_restart = true;
|
||||
|
||||
if(config.snd_rate < 1)
|
||||
config.snd_rate = 1;
|
||||
}
|
||||
|
||||
// volume slider
|
||||
{
|
||||
RECT button, label;
|
||||
ui_hsplit_t(&main_view, 5.0f, &button, &main_view);
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
ui_vsplit_l(&button, 110.0f, &label, &button);
|
||||
ui_hmargin(&button, 2.0f, &button);
|
||||
ui_do_label(&label, "Sound Volume", 14.0f, -1);
|
||||
config.snd_volume = (int)(ui_do_scrollbar_h(&config.snd_volume, &button, config.snd_volume/100.0f)*100.0f);
|
||||
ui_hsplit_t(&main_view, 20.0f, 0, &main_view);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
static void menu2_render_settings_network(RECT main_view)
|
||||
{
|
||||
RECT button;
|
||||
ui_vsplit_l(&main_view, 300.0f, &main_view, 0);
|
||||
|
||||
{
|
||||
ui_hsplit_t(&main_view, 20.0f, &button, &main_view);
|
||||
ui_do_label(&button, "Rcon Password", 14.0, -1);
|
||||
ui_vsplit_l(&button, 110.0f, 0, &button);
|
||||
ui_vsplit_l(&button, 180.0f, &button, 0);
|
||||
ui_do_edit_box(&config.rcon_password, &button, config.rcon_password, sizeof(config.rcon_password), true);
|
||||
}
|
||||
}*/
|
||||
|
||||
void MENUS::render_settings(RECT main_view)
|
||||
{
|
||||
static int settings_page = 0;
|
||||
|
||||
// render background
|
||||
RECT temp, tabbar;
|
||||
ui_vsplit_r(&main_view, 120.0f, &main_view, &tabbar);
|
||||
ui_draw_rect(&main_view, color_tabbar_active, CORNER_B|CORNER_TL, 10.0f);
|
||||
ui_hsplit_t(&tabbar, 50.0f, &temp, &tabbar);
|
||||
ui_draw_rect(&temp, color_tabbar_active, CORNER_R, 10.0f);
|
||||
|
||||
ui_hsplit_t(&main_view, 10.0f, 0, &main_view);
|
||||
|
||||
RECT button;
|
||||
|
||||
const char *tabs[] = {"Player", "Controls", "Graphics", "Sound"};
|
||||
int num_tabs = (int)(sizeof(tabs)/sizeof(*tabs));
|
||||
|
||||
for(int i = 0; i < num_tabs; i++)
|
||||
{
|
||||
ui_hsplit_t(&tabbar, 10, &button, &tabbar);
|
||||
ui_hsplit_t(&tabbar, 26, &button, &tabbar);
|
||||
if(ui_do_button(tabs[i], tabs[i], settings_page == i, &button, ui_draw_settings_tab_button, 0))
|
||||
settings_page = i;
|
||||
}
|
||||
|
||||
ui_margin(&main_view, 10.0f, &main_view);
|
||||
|
||||
if(settings_page == 0)
|
||||
render_settings_player(main_view);
|
||||
else if(settings_page == 1)
|
||||
render_settings_controls(main_view);
|
||||
else if(settings_page == 2)
|
||||
render_settings_graphics(main_view);
|
||||
else if(settings_page == 3)
|
||||
render_settings_sound(main_view);
|
||||
|
||||
if(need_restart)
|
||||
{
|
||||
RECT restart_warning;
|
||||
ui_hsplit_b(&main_view, 40, &main_view, &restart_warning);
|
||||
ui_do_label(&restart_warning, "You must restart the game for all settings to take effect.", 15.0f, -1, 220);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
|
||||
#include "motd.hpp"
|
||||
|
||||
void MOTD::on_reset()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void MOTD::clear()
|
||||
{
|
||||
server_motd_time = 0;
|
||||
}
|
||||
|
||||
bool MOTD::is_active()
|
||||
{
|
||||
return time_get() < server_motd_time;
|
||||
}
|
||||
|
||||
void MOTD::on_render()
|
||||
{
|
||||
if(!is_active())
|
||||
return;
|
||||
|
||||
float width = 400*3.0f*gfx_screenaspect();
|
||||
float height = 400*3.0f;
|
||||
|
||||
gfx_mapscreen(0, 0, width, height);
|
||||
|
||||
float h = 800.0f;
|
||||
float w = 650.0f;
|
||||
float x = width/2 - w/2;
|
||||
float y = 150.0f;
|
||||
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0,0,0,0.5f);
|
||||
draw_round_rect(x, y, w, h, 40.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
gfx_text(0, x+40.0f, y+40.0f, 32.0f, server_motd, (int)(w-80.0f));
|
||||
}
|
||||
|
||||
void MOTD::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_MOTD)
|
||||
{
|
||||
NETMSG_SV_MOTD *msg = (NETMSG_SV_MOTD *)rawmsg;
|
||||
|
||||
// process escaping
|
||||
str_copy(server_motd, msg->message, sizeof(server_motd));
|
||||
for(int i = 0; server_motd[i]; i++)
|
||||
{
|
||||
if(server_motd[i] == '\\')
|
||||
{
|
||||
if(server_motd[i+1] == 'n')
|
||||
{
|
||||
server_motd[i] = ' ';
|
||||
server_motd[i+1] = '\n';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(server_motd[0] && config.cl_motd_time)
|
||||
server_motd_time = time_get()+time_freq()*config.cl_motd_time;
|
||||
else
|
||||
server_motd_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool MOTD::on_input(INPUT_EVENT e)
|
||||
{
|
||||
if(is_active() && e.flags&INPFLAG_PRESS && e.key == KEY_ESCAPE)
|
||||
{
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class MOTD : public COMPONENT
|
||||
{
|
||||
// motd
|
||||
int64 server_motd_time;
|
||||
public:
|
||||
char server_motd[900];
|
||||
|
||||
void clear();
|
||||
bool is_active();
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
virtual bool on_input(INPUT_EVENT e);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
#include "nameplates.hpp"
|
||||
#include "controls.hpp"
|
||||
|
||||
void NAMEPLATES::render_nameplate(
|
||||
const NETOBJ_CHARACTER *prev_char,
|
||||
const NETOBJ_CHARACTER *player_char,
|
||||
const NETOBJ_PLAYER_INFO *player_info
|
||||
)
|
||||
{
|
||||
float intratick = client_intratick();
|
||||
|
||||
vec2 position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), intratick);
|
||||
|
||||
// render name plate
|
||||
if(!player_info->local)
|
||||
{
|
||||
//gfx_text_color
|
||||
float a = 1;
|
||||
if(config.cl_nameplates_always == 0)
|
||||
a = clamp(1-powf(distance(gameclient.controls->target_pos, position)/200.0f,16.0f), 0.0f, 1.0f);
|
||||
|
||||
const char *name = gameclient.clients[player_info->cid].name;
|
||||
float tw = gfx_text_width(0, 28.0f, name, -1);
|
||||
gfx_text_color(1,1,1,a);
|
||||
gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1);
|
||||
|
||||
if(config.debug) // render client id when in debug aswell
|
||||
{
|
||||
char buf[128];
|
||||
str_format(buf, sizeof(buf),"%d", player_info->cid);
|
||||
gfx_text(0, position.x, position.y-90, 28.0f, buf, -1);
|
||||
}
|
||||
|
||||
gfx_text_color(1,1,1,1);
|
||||
}
|
||||
}
|
||||
|
||||
void NAMEPLATES::on_render()
|
||||
{
|
||||
if (!config.cl_nameplates)
|
||||
return;
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
// only render active characters
|
||||
if(!gameclient.snap.characters[i].active)
|
||||
continue;
|
||||
|
||||
const void *info = snap_find_item(SNAP_CURRENT, NETOBJTYPE_PLAYER_INFO, i);
|
||||
|
||||
if(info)
|
||||
{
|
||||
render_nameplate(
|
||||
&gameclient.snap.characters[i].prev,
|
||||
&gameclient.snap.characters[i].cur,
|
||||
(const NETOBJ_PLAYER_INFO *)info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class NAMEPLATES : public COMPONENT
|
||||
{
|
||||
void render_nameplate(
|
||||
const class NETOBJ_CHARACTER *prev_char,
|
||||
const class NETOBJ_CHARACTER *player_char,
|
||||
const class NETOBJ_PLAYER_INFO *player_info
|
||||
);
|
||||
|
||||
public:
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
#include <base/math.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include <game/gamecore.hpp>
|
||||
#include "particles.hpp"
|
||||
|
||||
PARTICLES::PARTICLES()
|
||||
{
|
||||
on_reset();
|
||||
render_trail.parts = this;
|
||||
render_explosions.parts = this;
|
||||
render_general.parts = this;
|
||||
}
|
||||
|
||||
|
||||
void PARTICLES::on_reset()
|
||||
{
|
||||
// reset particles
|
||||
for(int i = 0; i < MAX_PARTICLES; i++)
|
||||
{
|
||||
particles[i].prev_part = i-1;
|
||||
particles[i].next_part = i+1;
|
||||
}
|
||||
|
||||
particles[0].prev_part = 0;
|
||||
particles[MAX_PARTICLES-1].next_part = -1;
|
||||
first_free = 0;
|
||||
|
||||
for(int i = 0; i < NUM_GROUPS; i++)
|
||||
first_part[i] = -1;
|
||||
}
|
||||
|
||||
void PARTICLES::add(int group, PARTICLE *part)
|
||||
{
|
||||
if (first_free == -1)
|
||||
return;
|
||||
|
||||
// remove from the free list
|
||||
int id = first_free;
|
||||
first_free = particles[id].next_part;
|
||||
particles[first_free].prev_part = -1;
|
||||
|
||||
// copy data
|
||||
particles[id] = *part;
|
||||
|
||||
// insert to the group list
|
||||
particles[id].prev_part = -1;
|
||||
particles[id].next_part = first_part[group];
|
||||
if(first_part[group] != -1)
|
||||
particles[first_part[group]].prev_part = id;
|
||||
first_part[group] = id;
|
||||
|
||||
// set some parameters
|
||||
particles[id].life = 0;
|
||||
}
|
||||
|
||||
void PARTICLES::update(float time_passed)
|
||||
{
|
||||
static float friction_fraction = 0;
|
||||
friction_fraction += time_passed;
|
||||
|
||||
if(friction_fraction > 2.0f) // safty messure
|
||||
friction_fraction = 0;
|
||||
|
||||
int friction_count = 0;
|
||||
while(friction_fraction > 0.05f)
|
||||
{
|
||||
friction_count++;
|
||||
friction_fraction -= 0.05f;
|
||||
}
|
||||
|
||||
for(int g = 0; g < NUM_GROUPS; g++)
|
||||
{
|
||||
int i = first_part[g];
|
||||
while(i != -1)
|
||||
{
|
||||
int next = particles[i].next_part;
|
||||
//particles[i].vel += flow_get(particles[i].pos)*time_passed * particles[i].flow_affected;
|
||||
particles[i].vel.y += particles[i].gravity*time_passed;
|
||||
|
||||
for(int f = 0; f < friction_count; f++) // apply friction
|
||||
particles[i].vel *= particles[i].friction;
|
||||
|
||||
// move the point
|
||||
vec2 vel = particles[i].vel*time_passed;
|
||||
move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL);
|
||||
particles[i].vel = vel* (1.0f/time_passed);
|
||||
|
||||
particles[i].life += time_passed;
|
||||
particles[i].rot += time_passed * particles[i].rotspeed;
|
||||
|
||||
// check particle death
|
||||
if(particles[i].life > particles[i].life_span)
|
||||
{
|
||||
// remove it from the group list
|
||||
if(particles[i].prev_part != -1)
|
||||
particles[particles[i].prev_part].next_part = particles[i].next_part;
|
||||
else
|
||||
first_part[g] = particles[i].next_part;
|
||||
|
||||
if(particles[i].next_part != -1)
|
||||
particles[particles[i].next_part].prev_part = particles[i].prev_part;
|
||||
|
||||
// insert to the free list
|
||||
if(first_free != -1)
|
||||
particles[first_free].prev_part = i;
|
||||
particles[i].prev_part = -1;
|
||||
particles[i].next_part = first_free;
|
||||
first_free = i;
|
||||
}
|
||||
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PARTICLES::on_render()
|
||||
{
|
||||
static int64 lasttime = 0;
|
||||
int64 t = time_get();
|
||||
update((float)((t-lasttime)/(double)time_freq()));
|
||||
lasttime = t;
|
||||
}
|
||||
|
||||
void PARTICLES::render_group(int group)
|
||||
{
|
||||
gfx_blend_normal();
|
||||
//gfx_blend_additive();
|
||||
gfx_texture_set(data->images[IMAGE_PARTICLES].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
int i = first_part[group];
|
||||
while(i != -1)
|
||||
{
|
||||
select_sprite(particles[i].spr);
|
||||
float a = particles[i].life / particles[i].life_span;
|
||||
vec2 p = particles[i].pos;
|
||||
float size = mix(particles[i].start_size, particles[i].end_size, a);
|
||||
|
||||
gfx_quads_setrotation(particles[i].rot);
|
||||
|
||||
gfx_setcolor(
|
||||
particles[i].color.r,
|
||||
particles[i].color.g,
|
||||
particles[i].color.b,
|
||||
particles[i].color.a); // pow(a, 0.75f) *
|
||||
|
||||
gfx_quads_draw(p.x, p.y, size, size);
|
||||
|
||||
i = particles[i].next_part;
|
||||
}
|
||||
gfx_quads_end();
|
||||
gfx_blend_normal();
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
// particles
|
||||
struct PARTICLE
|
||||
{
|
||||
void set_default()
|
||||
{
|
||||
vel = vec2(0,0);
|
||||
life_span = 0;
|
||||
start_size = 32;
|
||||
end_size = 32;
|
||||
rot = 0;
|
||||
rotspeed = 0;
|
||||
gravity = 0;
|
||||
friction = 0;
|
||||
flow_affected = 1.0f;
|
||||
color = vec4(1,1,1,1);
|
||||
}
|
||||
|
||||
vec2 pos;
|
||||
vec2 vel;
|
||||
|
||||
int spr;
|
||||
|
||||
float flow_affected;
|
||||
|
||||
float life_span;
|
||||
|
||||
float start_size;
|
||||
float end_size;
|
||||
|
||||
float rot;
|
||||
float rotspeed;
|
||||
|
||||
float gravity;
|
||||
float friction;
|
||||
|
||||
vec4 color;
|
||||
|
||||
// set by the particle system
|
||||
float life;
|
||||
int prev_part;
|
||||
int next_part;
|
||||
};
|
||||
|
||||
class PARTICLES : public COMPONENT
|
||||
{
|
||||
friend class GAMECLIENT;
|
||||
public:
|
||||
enum
|
||||
{
|
||||
GROUP_PROJECTILE_TRAIL=0,
|
||||
GROUP_EXPLOSIONS,
|
||||
GROUP_GENERAL,
|
||||
NUM_GROUPS
|
||||
};
|
||||
|
||||
PARTICLES();
|
||||
|
||||
void add(int group, PARTICLE *part);
|
||||
|
||||
virtual void on_reset();
|
||||
virtual void on_render();
|
||||
|
||||
private:
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_PARTICLES=1024*8,
|
||||
};
|
||||
|
||||
PARTICLE particles[MAX_PARTICLES];
|
||||
int first_free;
|
||||
int first_part[NUM_GROUPS];
|
||||
|
||||
void render_group(int group);
|
||||
void update(float time_passed);
|
||||
|
||||
template<int TGROUP>
|
||||
class RENDER_GROUP : public COMPONENT
|
||||
{
|
||||
public:
|
||||
PARTICLES *parts;
|
||||
virtual void on_render() { parts->render_group(TGROUP); }
|
||||
};
|
||||
|
||||
RENDER_GROUP<GROUP_PROJECTILE_TRAIL> render_trail;
|
||||
RENDER_GROUP<GROUP_EXPLOSIONS> render_explosions;
|
||||
RENDER_GROUP<GROUP_GENERAL> render_general;
|
||||
};
|
||||
@@ -0,0 +1,494 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/gamecore.hpp> // get_angle
|
||||
#include <game/client/animstate.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/ui.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
#include <game/client/components/flow.hpp>
|
||||
#include <game/client/components/skins.hpp>
|
||||
#include <game/client/components/effects.hpp>
|
||||
#include <game/client/components/sounds.hpp>
|
||||
#include <game/client/components/controls.hpp>
|
||||
|
||||
#include "players.hpp"
|
||||
|
||||
void PLAYERS::render_hand(TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset)
|
||||
{
|
||||
// for drawing hand
|
||||
//const skin *s = skin_get(skin_id);
|
||||
|
||||
float basesize = 10.0f;
|
||||
//dir = normalize(hook_pos-pos);
|
||||
|
||||
vec2 hand_pos = center_pos + dir;
|
||||
float angle = get_angle(dir);
|
||||
if (dir.x < 0)
|
||||
angle -= angle_offset;
|
||||
else
|
||||
angle += angle_offset;
|
||||
|
||||
vec2 dirx = dir;
|
||||
vec2 diry(-dir.y,dir.x);
|
||||
|
||||
if (dir.x < 0)
|
||||
diry = -diry;
|
||||
|
||||
hand_pos += dirx * post_rot_offset.x;
|
||||
hand_pos += diry * post_rot_offset.y;
|
||||
|
||||
//gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
|
||||
gfx_texture_set(info->texture);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a);
|
||||
|
||||
// two passes
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
bool outline = i == 0;
|
||||
|
||||
select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0);
|
||||
gfx_quads_setrotation(angle);
|
||||
gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize);
|
||||
}
|
||||
|
||||
gfx_quads_setrotation(0);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
inline float normalize_angular(float f)
|
||||
{
|
||||
return fmod(f+pi*2, pi*2);
|
||||
}
|
||||
|
||||
inline float angular_mix_direction(float src, float dst) { return sinf(dst-src) >0?1:-1; }
|
||||
inline float angular_distance(float src, float dst) { return asinf(sinf(dst-src)); }
|
||||
|
||||
inline float angular_approach(float src, float dst, float amount)
|
||||
{
|
||||
float d = angular_mix_direction(src, dst);
|
||||
float n = src + amount*d;
|
||||
if(angular_mix_direction(n, dst) != d)
|
||||
return dst;
|
||||
return n;
|
||||
}
|
||||
|
||||
void PLAYERS::render_player(
|
||||
const NETOBJ_CHARACTER *prev_char,
|
||||
const NETOBJ_CHARACTER *player_char,
|
||||
const NETOBJ_PLAYER_INFO *prev_info,
|
||||
const NETOBJ_PLAYER_INFO *player_info
|
||||
)
|
||||
{
|
||||
NETOBJ_CHARACTER prev;
|
||||
NETOBJ_CHARACTER player;
|
||||
prev = *prev_char;
|
||||
player = *player_char;
|
||||
|
||||
NETOBJ_PLAYER_INFO info = *player_info;
|
||||
TEE_RENDER_INFO render_info = gameclient.clients[info.cid].render_info;
|
||||
|
||||
// check for teamplay modes
|
||||
bool is_teamplay = false;
|
||||
bool new_tick = gameclient.new_tick;
|
||||
if(gameclient.snap.gameobj)
|
||||
is_teamplay = (gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS) != 0;
|
||||
|
||||
// check for ninja
|
||||
if (player.weapon == WEAPON_NINJA)
|
||||
{
|
||||
// change the skin for the player to the ninja
|
||||
int skin = gameclient.skins->find("x_ninja");
|
||||
if(skin != -1)
|
||||
{
|
||||
if(is_teamplay)
|
||||
render_info.texture = gameclient.skins->get(skin)->color_texture;
|
||||
else
|
||||
{
|
||||
render_info.texture = gameclient.skins->get(skin)->org_texture;
|
||||
render_info.color_body = vec4(1,1,1,1);
|
||||
render_info.color_feet = vec4(1,1,1,1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set size
|
||||
render_info.size = 64.0f;
|
||||
|
||||
float intratick = client_intratick();
|
||||
|
||||
if(player.health < 0) // dont render dead players
|
||||
return;
|
||||
|
||||
float angle = mix((float)prev.angle, (float)player.angle, intratick)/256.0f;
|
||||
|
||||
//float angle = 0;
|
||||
|
||||
if(info.local && client_state() != CLIENTSTATE_DEMOPLAYBACK)
|
||||
{
|
||||
// just use the direct input if it's local player we are rendering
|
||||
angle = get_angle(gameclient.controls->mouse_pos);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
float mixspeed = client_frametime()*2.5f;
|
||||
if(player.attacktick != prev.attacktick) // shooting boosts the mixing speed
|
||||
mixspeed *= 15.0f;
|
||||
|
||||
// move the delta on a constant speed on a x^2 curve
|
||||
float current = gameclient.clients[info.cid].angle;
|
||||
float target = player.angle/256.0f;
|
||||
float delta = angular_distance(current, target);
|
||||
float sign = delta < 0 ? -1 : 1;
|
||||
float new_delta = delta - 2*mixspeed*sqrt(delta*sign)*sign + mixspeed*mixspeed;
|
||||
|
||||
// make sure that it doesn't vibrate when it's still
|
||||
if(fabs(delta) < 2/256.0f)
|
||||
angle = target;
|
||||
else
|
||||
angle = angular_approach(current, target, fabs(delta-new_delta));
|
||||
|
||||
gameclient.clients[info.cid].angle = angle;*/
|
||||
}
|
||||
|
||||
// use preditect players if needed
|
||||
if(info.local && config.cl_predict && client_state() != CLIENTSTATE_DEMOPLAYBACK)
|
||||
{
|
||||
if(!gameclient.snap.local_character || (gameclient.snap.local_character->health < 0) || (gameclient.snap.gameobj && gameclient.snap.gameobj->game_over))
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply predicted results
|
||||
gameclient.predicted_char.write(&player);
|
||||
gameclient.predicted_prev_char.write(&prev);
|
||||
intratick = client_predintratick();
|
||||
new_tick = gameclient.new_predicted_tick;
|
||||
}
|
||||
}
|
||||
|
||||
vec2 direction = get_direction((int)(angle*256.0f));
|
||||
vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick);
|
||||
vec2 vel = mix(vec2(prev.vx/256.0f, prev.vy/256.0f), vec2(player.vx/256.0f, player.vy/256.0f), intratick);
|
||||
|
||||
gameclient.flow->add(position, vel*100.0f, 10.0f);
|
||||
|
||||
render_info.got_airjump = player.jumped&2?0:1;
|
||||
|
||||
|
||||
// detect events
|
||||
if(new_tick)
|
||||
{
|
||||
// detect air jump
|
||||
if(!render_info.got_airjump && !(prev.jumped&2))
|
||||
gameclient.effects->air_jump(position);
|
||||
}
|
||||
|
||||
if(prev.health < 0) // Don't flicker from previous position
|
||||
position = vec2(player.x, player.y);
|
||||
|
||||
bool stationary = player.vx <= 1 && player.vx >= -1;
|
||||
bool inair = col_check_point(player.x, player.y+16) == 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;
|
||||
ANIMSTATE state;
|
||||
state.set(&data->animations[ANIM_BASE], 0);
|
||||
|
||||
if(inair)
|
||||
state.add(&data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here
|
||||
else if(stationary)
|
||||
state.add(&data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here
|
||||
else if(!want_other_dir)
|
||||
state.add(&data->animations[ANIM_WALK], walk_time, 1.0f);
|
||||
|
||||
if (player.weapon == WEAPON_HAMMER)
|
||||
{
|
||||
float ct = (client_prevtick()-player.attacktick)/(float)SERVER_TICK_SPEED + client_ticktime();
|
||||
state.add(&data->animations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f);
|
||||
}
|
||||
if (player.weapon == WEAPON_NINJA)
|
||||
{
|
||||
float ct = (client_prevtick()-player.attacktick)/(float)SERVER_TICK_SPEED + client_ticktime();
|
||||
state.add(&data->animations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 1.0f);
|
||||
}
|
||||
|
||||
// do skidding
|
||||
if(!inair && want_other_dir && length(vel*50) > 500.0f)
|
||||
{
|
||||
static int64 skid_sound_time = 0;
|
||||
if(time_get()-skid_sound_time > time_freq()/10)
|
||||
{
|
||||
gameclient.sounds->play(SOUNDS::CHN_WORLD, SOUND_PLAYER_SKID, 0.25f, position);
|
||||
skid_sound_time = time_get();
|
||||
}
|
||||
|
||||
gameclient.effects->skidtrail(
|
||||
position+vec2(-player.direction*6,12),
|
||||
vec2(-player.direction*100*length(vel),-50)
|
||||
);
|
||||
}
|
||||
|
||||
// draw hook
|
||||
if (prev.hook_state>0 && player.hook_state>0)
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
//gfx_quads_begin();
|
||||
|
||||
vec2 pos = position;
|
||||
vec2 hook_pos;
|
||||
|
||||
if(player_char->hooked_player != -1)
|
||||
{
|
||||
if(gameclient.snap.local_info && player_char->hooked_player == gameclient.snap.local_info->cid)
|
||||
{
|
||||
hook_pos = mix(vec2(gameclient.predicted_prev_char.pos.x, gameclient.predicted_prev_char.pos.y),
|
||||
vec2(gameclient.predicted_char.pos.x, gameclient.predicted_char.pos.y), client_predintratick());
|
||||
}
|
||||
else
|
||||
hook_pos = mix(vec2(prev_char->hook_x, prev_char->hook_y), vec2(player_char->hook_x, player_char->hook_y), client_intratick());
|
||||
}
|
||||
else
|
||||
hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick);
|
||||
|
||||
float d = distance(pos, hook_pos);
|
||||
vec2 dir = normalize(pos-hook_pos);
|
||||
|
||||
gfx_quads_setrotation(get_angle(dir)+pi);
|
||||
|
||||
// render head
|
||||
select_sprite(SPRITE_HOOK_HEAD);
|
||||
gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16);
|
||||
|
||||
// render chain
|
||||
select_sprite(SPRITE_HOOK_CHAIN);
|
||||
int i = 0;
|
||||
for(float f = 24; f < d && i < 1024; f += 24, i++)
|
||||
{
|
||||
vec2 p = hook_pos + dir*f;
|
||||
gfx_quads_draw(p.x, p.y,24,16);
|
||||
}
|
||||
|
||||
gfx_quads_setrotation(0);
|
||||
gfx_quads_end();
|
||||
|
||||
render_hand(&render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0));
|
||||
}
|
||||
|
||||
// draw gun
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
gfx_quads_setrotation(state.attach.angle*pi*2+angle);
|
||||
|
||||
// normal weapons
|
||||
int iw = clamp(player.weapon, 0, NUM_WEAPONS-1);
|
||||
select_sprite(data->weapons.id[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
|
||||
|
||||
vec2 dir = direction;
|
||||
float recoil = 0.0f;
|
||||
vec2 p;
|
||||
if (player.weapon == WEAPON_HAMMER)
|
||||
{
|
||||
// Static position for hammer
|
||||
p = position + vec2(state.attach.x, state.attach.y);
|
||||
p.y += data->weapons.id[iw].offsety;
|
||||
// if attack is under way, bash stuffs
|
||||
if(direction.x < 0)
|
||||
{
|
||||
gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
|
||||
p.x -= data->weapons.id[iw].offsetx;
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2);
|
||||
}
|
||||
draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size);
|
||||
}
|
||||
else if (player.weapon == WEAPON_NINJA)
|
||||
{
|
||||
p = position;
|
||||
p.y += data->weapons.id[iw].offsety;
|
||||
|
||||
if(direction.x < 0)
|
||||
{
|
||||
gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
|
||||
p.x -= data->weapons.id[iw].offsetx;
|
||||
gameclient.effects->powerupshine(p+vec2(32,0), vec2(32,12));
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2);
|
||||
gameclient.effects->powerupshine(p-vec2(32,0), vec2(32,12));
|
||||
}
|
||||
draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size);
|
||||
|
||||
// HADOKEN
|
||||
if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons.id[iw].num_sprite_muzzles)
|
||||
{
|
||||
int itex = rand() % data->weapons.id[iw].num_sprite_muzzles;
|
||||
float alpha = 1.0f;
|
||||
if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex])
|
||||
{
|
||||
vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y);
|
||||
dir = normalize(dir);
|
||||
float hadokenangle = get_angle(dir);
|
||||
gfx_quads_setrotation(hadokenangle);
|
||||
//float offsety = -data->weapons[iw].muzzleoffsety;
|
||||
select_sprite(data->weapons.id[iw].sprite_muzzles[itex], 0);
|
||||
vec2 diry(-dir.y,dir.x);
|
||||
p = position;
|
||||
float offsetx = data->weapons.id[iw].muzzleoffsetx;
|
||||
p -= dir * offsetx;
|
||||
draw_sprite(p.x, p.y, 160.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: should be an animation
|
||||
recoil = 0;
|
||||
float a = (client_tick()-player.attacktick+intratick)/5.0f;
|
||||
if(a < 1)
|
||||
recoil = sinf(a*pi);
|
||||
p = position + dir * data->weapons.id[iw].offsetx - dir*recoil*10.0f;
|
||||
p.y += data->weapons.id[iw].offsety;
|
||||
draw_sprite(p.x, p.y, data->weapons.id[iw].visual_size);
|
||||
}
|
||||
|
||||
if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN)
|
||||
{
|
||||
// check if we're firing stuff
|
||||
if(data->weapons.id[iw].num_sprite_muzzles)//prev.attackticks)
|
||||
{
|
||||
float alpha = 0.0f;
|
||||
int phase1tick = (client_tick() - player.attacktick);
|
||||
if (phase1tick < (data->weapons.id[iw].muzzleduration + 3))
|
||||
{
|
||||
float t = ((((float)phase1tick) + intratick)/(float)data->weapons.id[iw].muzzleduration);
|
||||
alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t)));
|
||||
}
|
||||
|
||||
int itex = rand() % data->weapons.id[iw].num_sprite_muzzles;
|
||||
if (alpha > 0.0f && data->weapons.id[iw].sprite_muzzles[itex])
|
||||
{
|
||||
float offsety = -data->weapons.id[iw].muzzleoffsety;
|
||||
select_sprite(data->weapons.id[iw].sprite_muzzles[itex], direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
|
||||
if(direction.x < 0)
|
||||
offsety = -offsety;
|
||||
|
||||
vec2 diry(-dir.y,dir.x);
|
||||
vec2 muzzlepos = p + dir * data->weapons.id[iw].muzzleoffsetx + diry * offsety;
|
||||
|
||||
draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons.id[iw].visual_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
gfx_quads_end();
|
||||
|
||||
switch (player.weapon)
|
||||
{
|
||||
case WEAPON_GUN: render_hand(&render_info, p, direction, -3*pi/4, vec2(-15, 4)); break;
|
||||
case WEAPON_SHOTGUN: render_hand(&render_info, p, direction, -pi/2, vec2(-5, 4)); break;
|
||||
case WEAPON_GRENADE: render_hand(&render_info, p, direction, -pi/2, vec2(-4, 7)); break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// render the "shadow" tee
|
||||
if(info.local && config.debug)
|
||||
{
|
||||
vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick());
|
||||
TEE_RENDER_INFO ghost = render_info;
|
||||
ghost.color_body.a = 0.5f;
|
||||
ghost.color_feet.a = 0.5f;
|
||||
render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost
|
||||
}
|
||||
|
||||
render_info.size = 64.0f; // force some settings
|
||||
render_info.color_body.a = 1.0f;
|
||||
render_info.color_feet.a = 1.0f;
|
||||
render_tee(&state, &render_info, player.emote, direction, position);
|
||||
|
||||
if(player.player_state == PLAYERSTATE_CHATTING)
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
|
||||
gfx_quads_begin();
|
||||
select_sprite(SPRITE_DOTDOT);
|
||||
gfx_quads_draw(position.x + 24, position.y - 40, 64,64);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
if (gameclient.clients[info.cid].emoticon_start != -1 && gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() > client_tick())
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
int since_start = client_tick() - gameclient.clients[info.cid].emoticon_start;
|
||||
int from_end = gameclient.clients[info.cid].emoticon_start + 2 * client_tickspeed() - client_tick();
|
||||
|
||||
float a = 1;
|
||||
|
||||
if (from_end < client_tickspeed() / 5)
|
||||
a = from_end / (client_tickspeed() / 5.0);
|
||||
|
||||
float h = 1;
|
||||
if (since_start < client_tickspeed() / 10)
|
||||
h = since_start / (client_tickspeed() / 10.0);
|
||||
|
||||
float wiggle = 0;
|
||||
if (since_start < client_tickspeed() / 5)
|
||||
wiggle = since_start / (client_tickspeed() / 5.0);
|
||||
|
||||
float wiggle_angle = sin(5*wiggle);
|
||||
|
||||
gfx_quads_setrotation(pi/6*wiggle_angle);
|
||||
|
||||
gfx_setcolor(1.0f,1.0f,1.0f,a);
|
||||
// client_datas::emoticon is an offset from the first emoticon
|
||||
select_sprite(SPRITE_OOP + gameclient.clients[info.cid].emoticon);
|
||||
gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
|
||||
gfx_quads_end();
|
||||
}
|
||||
}
|
||||
|
||||
void PLAYERS::on_render()
|
||||
{
|
||||
// render other players in two passes, first pass we render the other, second pass we render our self
|
||||
for(int p = 0; p < 2; p++)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
// only render active characters
|
||||
if(!gameclient.snap.characters[i].active)
|
||||
continue;
|
||||
|
||||
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)
|
||||
{
|
||||
//
|
||||
bool local = ((const NETOBJ_PLAYER_INFO *)info)->local !=0;
|
||||
if(p == 0 && local) continue;
|
||||
if(p == 1 && !local) continue;
|
||||
|
||||
NETOBJ_CHARACTER prev_char = gameclient.snap.characters[i].prev;
|
||||
NETOBJ_CHARACTER cur_char = gameclient.snap.characters[i].cur;
|
||||
|
||||
render_player(
|
||||
&prev_char,
|
||||
&cur_char,
|
||||
(const NETOBJ_PLAYER_INFO *)prev_info,
|
||||
(const NETOBJ_PLAYER_INFO *)info
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class PLAYERS : public COMPONENT
|
||||
{
|
||||
void render_hand(class TEE_RENDER_INFO *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset);
|
||||
void render_player(
|
||||
const class NETOBJ_CHARACTER *prev_char,
|
||||
const class NETOBJ_CHARACTER *player_char,
|
||||
const class NETOBJ_PLAYER_INFO *prev_info,
|
||||
const class NETOBJ_PLAYER_INFO *player_info
|
||||
);
|
||||
|
||||
public:
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
#include <string.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/animstate.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include <game/client/components/motd.hpp>
|
||||
#include "scoreboard.hpp"
|
||||
|
||||
|
||||
SCOREBOARD::SCOREBOARD()
|
||||
{
|
||||
on_reset();
|
||||
}
|
||||
|
||||
void SCOREBOARD::con_key_scoreboard(void *result, void *user_data)
|
||||
{
|
||||
((SCOREBOARD *)user_data)->active = console_arg_int(result, 0) != 0;
|
||||
}
|
||||
|
||||
void SCOREBOARD::on_reset()
|
||||
{
|
||||
active = false;
|
||||
}
|
||||
|
||||
void SCOREBOARD::on_console_init()
|
||||
{
|
||||
MACRO_REGISTER_COMMAND("+scoreboard", "", CFGFLAG_CLIENT, con_key_scoreboard, this, "Show scoreboard");
|
||||
}
|
||||
|
||||
void SCOREBOARD::render_goals(float x, float y, float w)
|
||||
{
|
||||
float h = 50.0f;
|
||||
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0,0,0,0.5f);
|
||||
draw_round_rect(x-10.f, y-10.f, w, h, 10.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
// render goals
|
||||
//y = ystart+h-54;
|
||||
float tw = 0.0f;
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->score_limit)
|
||||
{
|
||||
char buf[64];
|
||||
str_format(buf, sizeof(buf), "Score Limit: %d", gameclient.snap.gameobj->score_limit);
|
||||
gfx_text(0, x+20.0f, y, 22.0f, buf, -1);
|
||||
tw += gfx_text_width(0, 22.0f, buf, -1);
|
||||
}
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->time_limit)
|
||||
{
|
||||
char buf[64];
|
||||
str_format(buf, sizeof(buf), "Time Limit: %d min", gameclient.snap.gameobj->time_limit);
|
||||
gfx_text(0, x+220.0f, y, 22.0f, buf, -1);
|
||||
tw += gfx_text_width(0, 22.0f, buf, -1);
|
||||
}
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->round_num && gameclient.snap.gameobj->round_current)
|
||||
{
|
||||
char buf[64];
|
||||
str_format(buf, sizeof(buf), "Round %d/%d", gameclient.snap.gameobj->round_current, gameclient.snap.gameobj->round_num);
|
||||
gfx_text(0, x+450.0f, y, 22.0f, buf, -1);
|
||||
|
||||
/*[48c3fd4c][game/scoreboard]: timelimit x:219.428558
|
||||
[48c3fd4c][game/scoreboard]: round x:453.142822*/
|
||||
}
|
||||
}
|
||||
|
||||
void SCOREBOARD::render_spectators(float x, float y, float w)
|
||||
{
|
||||
char buffer[1024*4];
|
||||
int count = 0;
|
||||
float h = 120.0f;
|
||||
|
||||
str_copy(buffer, "Spectators: ", sizeof(buffer));
|
||||
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0,0,0,0.5f);
|
||||
draw_round_rect(x-10.f, y-10.f, w, h, 10.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
|
||||
if(item.type == NETOBJTYPE_PLAYER_INFO)
|
||||
{
|
||||
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
|
||||
if(info->team == -1)
|
||||
{
|
||||
if(count)
|
||||
strcat(buffer, ", ");
|
||||
strcat(buffer, gameclient.clients[info->cid].name);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gfx_text(0, x+10, y, 32, buffer, (int)w-20);
|
||||
}
|
||||
|
||||
void SCOREBOARD::render_scoreboard(float x, float y, float w, int team, const char *title)
|
||||
{
|
||||
//float ystart = y;
|
||||
float h = 750.0f;
|
||||
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(0,0,0,0.5f);
|
||||
draw_round_rect(x-10.f, y-10.f, w, h, 17.0f);
|
||||
gfx_quads_end();
|
||||
|
||||
// render title
|
||||
if(!title)
|
||||
{
|
||||
if(gameclient.snap.gameobj->game_over)
|
||||
title = "Game Over";
|
||||
else
|
||||
title = "Score Board";
|
||||
}
|
||||
|
||||
float tw = gfx_text_width(0, 48, title, -1);
|
||||
|
||||
if(team == -1)
|
||||
{
|
||||
gfx_text(0, x+w/2-tw/2, y, 48, title, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
gfx_text(0, x+10, y, 48, title, -1);
|
||||
|
||||
if(gameclient.snap.gameobj)
|
||||
{
|
||||
char buf[128];
|
||||
int score = team ? gameclient.snap.gameobj->teamscore_blue : gameclient.snap.gameobj->teamscore_red;
|
||||
str_format(buf, sizeof(buf), "%d", score);
|
||||
tw = gfx_text_width(0, 48, buf, -1);
|
||||
gfx_text(0, x+w-tw-30, y, 48, buf, -1);
|
||||
}
|
||||
}
|
||||
|
||||
y += 54.0f;
|
||||
|
||||
// find players
|
||||
const NETOBJ_PLAYER_INFO *players[MAX_CLIENTS] = {0};
|
||||
int num_players = 0;
|
||||
for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
|
||||
if(item.type == NETOBJTYPE_PLAYER_INFO)
|
||||
{
|
||||
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
|
||||
if(info->team == team)
|
||||
{
|
||||
players[num_players] = info;
|
||||
num_players++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort players
|
||||
for(int k = 0; k < num_players; k++) // ffs, bubblesort
|
||||
{
|
||||
for(int i = 0; i < num_players-k-1; i++)
|
||||
{
|
||||
if(players[i]->score < players[i+1]->score)
|
||||
{
|
||||
const NETOBJ_PLAYER_INFO *tmp = players[i];
|
||||
players[i] = players[i+1];
|
||||
players[i+1] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render headlines
|
||||
gfx_text(0, x+10, y, 24.0f, "Score", -1);
|
||||
gfx_text(0, x+125, y, 24.0f, "Name", -1);
|
||||
gfx_text(0, x+w-70, y, 24.0f, "Ping", -1);
|
||||
y += 29.0f;
|
||||
|
||||
float font_size = 35.0f;
|
||||
float line_height = 50.0f;
|
||||
float tee_sizemod = 1.0f;
|
||||
float tee_offset = 0.0f;
|
||||
|
||||
if(num_players > 13)
|
||||
{
|
||||
font_size = 30.0f;
|
||||
line_height = 40.0f;
|
||||
tee_sizemod = 0.8f;
|
||||
tee_offset = -5.0f;
|
||||
}
|
||||
|
||||
// render player scores
|
||||
for(int i = 0; i < num_players; i++)
|
||||
{
|
||||
const NETOBJ_PLAYER_INFO *info = players[i];
|
||||
|
||||
// make sure that we render the correct team
|
||||
|
||||
char buf[128];
|
||||
if(info->local)
|
||||
{
|
||||
// background so it's easy to find the local player
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(1,1,1,0.25f);
|
||||
draw_round_rect(x, y, w-20, line_height*0.95f, 17.0f);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
str_format(buf, sizeof(buf), "%4d", info->score);
|
||||
gfx_text(0, x+60-gfx_text_width(0, font_size,buf,-1), y, font_size, buf, -1);
|
||||
|
||||
gfx_text(0, x+128, y, font_size, gameclient.clients[info->cid].name, -1);
|
||||
|
||||
str_format(buf, sizeof(buf), "%4d", info->latency);
|
||||
float tw = gfx_text_width(0, font_size, buf, -1);
|
||||
gfx_text(0, x+w-tw-35, y, font_size, buf, -1);
|
||||
|
||||
// render avatar
|
||||
if((gameclient.snap.flags[0] && gameclient.snap.flags[0]->carried_by == info->cid) ||
|
||||
(gameclient.snap.flags[1] && gameclient.snap.flags[1]->carried_by == info->cid))
|
||||
{
|
||||
gfx_blend_normal();
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
if(info->team == 0) select_sprite(SPRITE_FLAG_BLUE, SPRITE_FLAG_FLIP_X);
|
||||
else select_sprite(SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X);
|
||||
|
||||
float size = 64.0f;
|
||||
gfx_quads_drawTL(x+55, y-15, size/2, size);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
TEE_RENDER_INFO teeinfo = gameclient.clients[info->cid].render_info;
|
||||
teeinfo.size *= tee_sizemod;
|
||||
render_tee(ANIMSTATE::get_idle(), &teeinfo, EMOTE_NORMAL, vec2(1,0), vec2(x+90, y+28+tee_offset));
|
||||
|
||||
|
||||
y += line_height;
|
||||
}
|
||||
}
|
||||
|
||||
void SCOREBOARD::on_render()
|
||||
{
|
||||
bool do_scoreboard = false;
|
||||
|
||||
// if we activly wanna look on the scoreboard
|
||||
if(active)
|
||||
do_scoreboard = true;
|
||||
|
||||
if(gameclient.snap.local_info && gameclient.snap.local_info->team != -1)
|
||||
{
|
||||
// we are not a spectator, check if we are ead
|
||||
if(!gameclient.snap.local_character || gameclient.snap.local_character->health < 0)
|
||||
do_scoreboard = true;
|
||||
}
|
||||
|
||||
// if we the game is over
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)
|
||||
do_scoreboard = true;
|
||||
|
||||
if(!do_scoreboard)
|
||||
return;
|
||||
|
||||
// if the score board is active, then we should clear the motd message aswell
|
||||
if(active)
|
||||
gameclient.motd->clear();
|
||||
|
||||
|
||||
float width = 400*3.0f*gfx_screenaspect();
|
||||
float height = 400*3.0f;
|
||||
|
||||
gfx_mapscreen(0, 0, width, height);
|
||||
|
||||
float w = 650.0f;
|
||||
|
||||
if(gameclient.snap.gameobj && !(gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS))
|
||||
{
|
||||
render_scoreboard(width/2-w/2, 150.0f, w, 0, 0);
|
||||
//render_scoreboard(gameobj, 0, 0, -1, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->game_over)
|
||||
{
|
||||
const char *text = "DRAW!";
|
||||
if(gameclient.snap.gameobj->teamscore_red > gameclient.snap.gameobj->teamscore_blue)
|
||||
text = "Red Team Wins!";
|
||||
else if(gameclient.snap.gameobj->teamscore_blue > gameclient.snap.gameobj->teamscore_red)
|
||||
text = "Blue Team Wins!";
|
||||
|
||||
float w = gfx_text_width(0, 92.0f, text, -1);
|
||||
gfx_text(0, width/2-w/2, 45, 92.0f, text, -1);
|
||||
}
|
||||
|
||||
render_scoreboard(width/2-w-20, 150.0f, w, 0, "Red Team");
|
||||
render_scoreboard(width/2 + 20, 150.0f, w, 1, "Blue Team");
|
||||
}
|
||||
|
||||
render_goals(width/2-w/2, 150+750+25, w);
|
||||
render_spectators(width/2-w/2, 150+750+25+50+25, w);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class SCOREBOARD : public COMPONENT
|
||||
{
|
||||
void render_goals(float x, float y, float w);
|
||||
void render_spectators(float x, float y, float w);
|
||||
void render_scoreboard(float x, float y, float w, int team, const char *title);
|
||||
|
||||
static void con_key_scoreboard(void *result, void *user_data);
|
||||
|
||||
bool active;
|
||||
|
||||
public:
|
||||
SCOREBOARD();
|
||||
virtual void on_reset();
|
||||
virtual void on_console_init();
|
||||
virtual void on_render();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <base/system.h>
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <engine/e_engine.h>
|
||||
}
|
||||
|
||||
#include "skins.hpp"
|
||||
|
||||
SKINS::SKINS()
|
||||
{
|
||||
num_skins = 0;
|
||||
}
|
||||
|
||||
void SKINS::skinscan(const char *name, int is_dir, void *user)
|
||||
{
|
||||
SKINS *self = (SKINS *)user;
|
||||
int l = strlen(name);
|
||||
if(l < 4 || is_dir || self->num_skins == MAX_SKINS)
|
||||
return;
|
||||
if(strcmp(name+l-4, ".png") != 0)
|
||||
return;
|
||||
|
||||
char buf[512];
|
||||
str_format(buf, sizeof(buf), "skins/%s", name);
|
||||
IMAGE_INFO info;
|
||||
if(!gfx_load_png(&info, buf))
|
||||
{
|
||||
dbg_msg("game", "failed to load skin from %s", name);
|
||||
return;
|
||||
}
|
||||
|
||||
self->skins[self->num_skins].org_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0);
|
||||
|
||||
int body_size = 96; // body size
|
||||
unsigned char *d = (unsigned char *)info.data;
|
||||
int pitch = info.width*4;
|
||||
|
||||
// dig out blood color
|
||||
{
|
||||
int colors[3] = {0};
|
||||
for(int y = 0; y < body_size; y++)
|
||||
for(int x = 0; x < body_size; x++)
|
||||
{
|
||||
if(d[y*pitch+x*4+3] > 128)
|
||||
{
|
||||
colors[0] += d[y*pitch+x*4+0];
|
||||
colors[1] += d[y*pitch+x*4+1];
|
||||
colors[2] += d[y*pitch+x*4+2];
|
||||
}
|
||||
}
|
||||
|
||||
self->skins[self->num_skins].blood_color = normalize(vec3(colors[0], colors[1], colors[2]));
|
||||
}
|
||||
|
||||
// create colorless version
|
||||
int step = info.format == IMG_RGBA ? 4 : 3;
|
||||
|
||||
// make the texture gray scale
|
||||
for(int i = 0; i < info.width*info.height; i++)
|
||||
{
|
||||
int v = (d[i*step]+d[i*step+1]+d[i*step+2])/3;
|
||||
d[i*step] = v;
|
||||
d[i*step+1] = v;
|
||||
d[i*step+2] = v;
|
||||
}
|
||||
|
||||
|
||||
if(1)
|
||||
{
|
||||
int freq[256] = {0};
|
||||
int org_weight = 0;
|
||||
int new_weight = 192;
|
||||
|
||||
// find most common frequence
|
||||
for(int y = 0; y < body_size; y++)
|
||||
for(int x = 0; x < body_size; x++)
|
||||
{
|
||||
if(d[y*pitch+x*4+3] > 128)
|
||||
freq[d[y*pitch+x*4]]++;
|
||||
}
|
||||
|
||||
for(int i = 1; i < 256; i++)
|
||||
{
|
||||
if(freq[org_weight] < freq[i])
|
||||
org_weight = i;
|
||||
}
|
||||
|
||||
// reorder
|
||||
int inv_org_weight = 255-org_weight;
|
||||
int inv_new_weight = 255-new_weight;
|
||||
for(int y = 0; y < body_size; y++)
|
||||
for(int x = 0; x < body_size; x++)
|
||||
{
|
||||
int v = d[y*pitch+x*4];
|
||||
if(v <= org_weight)
|
||||
v = (int)(((v/(float)org_weight) * new_weight));
|
||||
else
|
||||
v = (int)(((v-org_weight)/(float)inv_org_weight)*inv_new_weight + new_weight);
|
||||
d[y*pitch+x*4] = v;
|
||||
d[y*pitch+x*4+1] = v;
|
||||
d[y*pitch+x*4+2] = v;
|
||||
}
|
||||
}
|
||||
|
||||
self->skins[self->num_skins].color_texture = gfx_load_texture_raw(info.width, info.height, info.format, info.data, info.format, 0);
|
||||
mem_free(info.data);
|
||||
|
||||
// set skin data
|
||||
strncpy(self->skins[self->num_skins].name, name, min((int)sizeof(self->skins[self->num_skins].name),l-4));
|
||||
dbg_msg("game", "load skin %s", self->skins[self->num_skins].name);
|
||||
self->num_skins++;
|
||||
}
|
||||
|
||||
|
||||
void SKINS::init()
|
||||
{
|
||||
// load skins
|
||||
num_skins = 0;
|
||||
engine_listdir(LISTDIRTYPE_ALL, "skins", skinscan, this);
|
||||
}
|
||||
|
||||
int SKINS::num()
|
||||
{
|
||||
return num_skins;
|
||||
}
|
||||
|
||||
const SKINS::SKIN *SKINS::get(int index)
|
||||
{
|
||||
return &skins[index%num_skins];
|
||||
}
|
||||
|
||||
int SKINS::find(const char *name)
|
||||
{
|
||||
for(int i = 0; i < num_skins; i++)
|
||||
{
|
||||
if(strcmp(skins[i].name, name) == 0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// these converter functions were nicked from some random internet pages
|
||||
static float hue_to_rgb(float v1, float v2, float h)
|
||||
{
|
||||
if(h < 0) h += 1;
|
||||
if(h > 1) h -= 1;
|
||||
if((6 * h) < 1) return v1 + ( v2 - v1 ) * 6 * h;
|
||||
if((2 * h) < 1) return v2;
|
||||
if((3 * h) < 2) return v1 + ( v2 - v1 ) * ((2.0f/3.0f) - h) * 6;
|
||||
return v1;
|
||||
}
|
||||
|
||||
static vec3 hsl_to_rgb(vec3 in)
|
||||
{
|
||||
float v1, v2;
|
||||
vec3 out;
|
||||
|
||||
if(in.s == 0)
|
||||
{
|
||||
out.r = in.l;
|
||||
out.g = in.l;
|
||||
out.b = in.l;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(in.l < 0.5f)
|
||||
v2 = in.l * (1 + in.s);
|
||||
else
|
||||
v2 = (in.l+in.s) - (in.s*in.l);
|
||||
|
||||
v1 = 2 * in.l - v2;
|
||||
|
||||
out.r = hue_to_rgb(v1, v2, in.h + (1.0f/3.0f));
|
||||
out.g = hue_to_rgb(v1, v2, in.h);
|
||||
out.b = hue_to_rgb(v1, v2, in.h - (1.0f/3.0f));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
vec4 SKINS::get_color(int v)
|
||||
{
|
||||
vec3 r = hsl_to_rgb(vec3((v>>16)/255.0f, ((v>>8)&0xff)/255.0f, 0.5f+(v&0xff)/255.0f*0.5f));
|
||||
return vec4(r.r, r.g, r.b, 1.0f);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class SKINS : public COMPONENT
|
||||
{
|
||||
public:
|
||||
// do this better and nicer
|
||||
typedef struct
|
||||
{
|
||||
int org_texture;
|
||||
int color_texture;
|
||||
char name[31];
|
||||
char term[1];
|
||||
vec3 blood_color;
|
||||
} SKIN;
|
||||
|
||||
SKINS();
|
||||
|
||||
void init();
|
||||
|
||||
vec4 get_color(int v);
|
||||
int num();
|
||||
const SKIN *get(int index);
|
||||
int find(const char *name);
|
||||
|
||||
private:
|
||||
enum
|
||||
{
|
||||
MAX_SKINS=256,
|
||||
};
|
||||
|
||||
SKIN skins[MAX_SKINS];
|
||||
int num_skins;
|
||||
|
||||
static void skinscan(const char *name, int is_dir, void *user);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
#include <game/client/gameclient.hpp>
|
||||
#include <game/client/components/camera.hpp>
|
||||
#include "sounds.hpp"
|
||||
|
||||
void SOUNDS::on_init()
|
||||
{
|
||||
// setup sound channels
|
||||
snd_set_channel(SOUNDS::CHN_GUI, 1.0f, 0.0f);
|
||||
snd_set_channel(SOUNDS::CHN_MUSIC, 1.0f, 0.0f);
|
||||
snd_set_channel(SOUNDS::CHN_WORLD, 0.9f, 1.0f);
|
||||
snd_set_channel(SOUNDS::CHN_GLOBAL, 1.0f, 0.0f);
|
||||
|
||||
snd_set_listener_pos(0.0f, 0.0f);
|
||||
}
|
||||
|
||||
void SOUNDS::on_render()
|
||||
{
|
||||
// set listner pos
|
||||
snd_set_listener_pos(gameclient.camera->center.x, gameclient.camera->center.y);
|
||||
}
|
||||
|
||||
void SOUNDS::play_and_record(int chn, int setid, float vol, vec2 pos)
|
||||
{
|
||||
NETMSG_SV_SOUNDGLOBAL msg;
|
||||
msg.soundid = setid;
|
||||
msg.pack(MSGFLAG_NOSEND|MSGFLAG_RECORD);
|
||||
client_send_msg();
|
||||
|
||||
play(chn, setid, vol, pos);
|
||||
}
|
||||
|
||||
void SOUNDS::play(int chn, int setid, float vol, vec2 pos)
|
||||
{
|
||||
SOUNDSET *set = &data->sounds[setid];
|
||||
|
||||
if(!set->num_sounds)
|
||||
return;
|
||||
|
||||
if(set->num_sounds == 1)
|
||||
{
|
||||
snd_play_at(chn, set->sounds[0].id, 0, pos.x, pos.y);
|
||||
return;
|
||||
}
|
||||
|
||||
// play a random one
|
||||
int id;
|
||||
do {
|
||||
id = rand() % set->num_sounds;
|
||||
} while(id == set->last);
|
||||
snd_play_at(chn, set->sounds[id].id, 0, pos.x, pos.y);
|
||||
set->last = id;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include <game/client/component.hpp>
|
||||
|
||||
class SOUNDS : public COMPONENT
|
||||
{
|
||||
public:
|
||||
// sound channels
|
||||
enum
|
||||
{
|
||||
CHN_GUI=0,
|
||||
CHN_MUSIC,
|
||||
CHN_WORLD,
|
||||
CHN_GLOBAL,
|
||||
};
|
||||
|
||||
virtual void on_init();
|
||||
virtual void on_render();
|
||||
|
||||
void play(int chn, int setid, float vol, vec2 pos);
|
||||
void play_and_record(int chn, int setid, float vol, vec2 pos);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
//#include <game/client/gameclient.hpp>
|
||||
#include "voting.hpp"
|
||||
|
||||
void VOTING::con_callvote(void *result, void *user_data)
|
||||
{
|
||||
VOTING *self = (VOTING*)user_data;
|
||||
self->callvote(console_arg_string(result, 0), console_arg_string(result, 1));
|
||||
}
|
||||
|
||||
void VOTING::con_vote(void *result, void *user_data)
|
||||
{
|
||||
VOTING *self = (VOTING *)user_data;
|
||||
if(str_comp_nocase(console_arg_string(result, 0), "yes") == 0)
|
||||
self->vote(1);
|
||||
else if(str_comp_nocase(console_arg_string(result, 0), "no") == 0)
|
||||
self->vote(-1);
|
||||
}
|
||||
|
||||
void VOTING::callvote(const char *type, const char *value)
|
||||
{
|
||||
NETMSG_CL_CALLVOTE msg = {0};
|
||||
msg.type = type;
|
||||
msg.value = value;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
|
||||
void VOTING::callvote_kick(int client_id)
|
||||
{
|
||||
char buf[32];
|
||||
str_format(buf, sizeof(buf), "%d", client_id);
|
||||
callvote("kick", buf);
|
||||
}
|
||||
|
||||
void VOTING::callvote_option(int option_id)
|
||||
{
|
||||
VOTEOPTION *option = this->first;
|
||||
while(option && option_id >= 0)
|
||||
{
|
||||
if(option_id == 0)
|
||||
{
|
||||
callvote("option", option->command);
|
||||
break;
|
||||
}
|
||||
|
||||
option_id--;
|
||||
option = option->next;
|
||||
}
|
||||
}
|
||||
|
||||
void VOTING::vote(int v)
|
||||
{
|
||||
NETMSG_CL_VOTE msg = {v};
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
|
||||
VOTING::VOTING()
|
||||
{
|
||||
heap = 0;
|
||||
clearoptions();
|
||||
on_reset();
|
||||
}
|
||||
|
||||
|
||||
void VOTING::clearoptions()
|
||||
{
|
||||
if(heap)
|
||||
memheap_destroy(heap);
|
||||
heap = memheap_create();
|
||||
|
||||
first = 0;
|
||||
last = 0;
|
||||
}
|
||||
|
||||
void VOTING::on_reset()
|
||||
{
|
||||
closetime = 0;
|
||||
description[0] = 0;
|
||||
command[0] = 0;
|
||||
yes = no = pass = total = 0;
|
||||
voted = 0;
|
||||
}
|
||||
|
||||
void VOTING::on_console_init()
|
||||
{
|
||||
MACRO_REGISTER_COMMAND("callvote", "sr", CFGFLAG_CLIENT, con_callvote, this, "Call vote");
|
||||
MACRO_REGISTER_COMMAND("vote", "r", CFGFLAG_CLIENT, con_vote, this, "Vote yes/no");
|
||||
}
|
||||
|
||||
void VOTING::on_message(int msgtype, void *rawmsg)
|
||||
{
|
||||
if(msgtype == NETMSGTYPE_SV_VOTE_SET)
|
||||
{
|
||||
NETMSG_SV_VOTE_SET *msg = (NETMSG_SV_VOTE_SET *)rawmsg;
|
||||
if(msg->timeout)
|
||||
{
|
||||
on_reset();
|
||||
str_copy(description, msg->description, sizeof(description));
|
||||
str_copy(command, msg->command, sizeof(description));
|
||||
closetime = time_get() + time_freq() * msg->timeout;
|
||||
}
|
||||
else
|
||||
on_reset();
|
||||
}
|
||||
else if(msgtype == NETMSGTYPE_SV_VOTE_STATUS)
|
||||
{
|
||||
NETMSG_SV_VOTE_STATUS *msg = (NETMSG_SV_VOTE_STATUS *)rawmsg;
|
||||
yes = msg->yes;
|
||||
no = msg->no;
|
||||
pass = msg->pass;
|
||||
total = msg->total;
|
||||
}
|
||||
else if(msgtype == NETMSGTYPE_SV_VOTE_CLEAROPTIONS)
|
||||
{
|
||||
clearoptions();
|
||||
}
|
||||
else if(msgtype == NETMSGTYPE_SV_VOTE_OPTION)
|
||||
{
|
||||
NETMSG_SV_VOTE_OPTION *msg = (NETMSG_SV_VOTE_OPTION *)rawmsg;
|
||||
int len = str_length(msg->command);
|
||||
|
||||
VOTEOPTION *option = (VOTEOPTION *)memheap_allocate(heap, sizeof(VOTEOPTION) + len);
|
||||
option->next = 0;
|
||||
option->prev = last;
|
||||
if(option->prev)
|
||||
option->prev->next = option;
|
||||
last = option;
|
||||
if(!first)
|
||||
first = option;
|
||||
|
||||
mem_copy(option->command, msg->command, len+1);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void VOTING::on_render()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void VOTING::render_bars(RECT bars, bool text)
|
||||
{
|
||||
ui_draw_rect(&bars, vec4(0.8f,0.8f,0.8f,0.5f), CORNER_ALL, bars.h/3);
|
||||
|
||||
RECT splitter = bars;
|
||||
splitter.x = splitter.x+splitter.w/2;
|
||||
splitter.w = splitter.h/2.0f;
|
||||
splitter.x -= splitter.w/2;
|
||||
ui_draw_rect(&splitter, vec4(0.4f,0.4f,0.4f,0.5f), CORNER_ALL, splitter.h/4);
|
||||
|
||||
if(total)
|
||||
{
|
||||
RECT pass_area = bars;
|
||||
if(yes)
|
||||
{
|
||||
RECT yes_area = bars;
|
||||
yes_area.w *= yes/(float)total;
|
||||
ui_draw_rect(&yes_area, vec4(0.2f,0.9f,0.2f,0.85f), CORNER_ALL, bars.h/3);
|
||||
|
||||
if(text)
|
||||
{
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "%d", yes);
|
||||
ui_do_label(&yes_area, buf, bars.h*0.75f, 0);
|
||||
}
|
||||
|
||||
pass_area.x += yes_area.w;
|
||||
pass_area.w -= yes_area.w;
|
||||
}
|
||||
|
||||
if(no)
|
||||
{
|
||||
RECT no_area = bars;
|
||||
no_area.w *= no/(float)total;
|
||||
no_area.x = (bars.x + bars.w)-no_area.w;
|
||||
ui_draw_rect(&no_area, vec4(0.9f,0.2f,0.2f,0.85f), CORNER_ALL, bars.h/3);
|
||||
|
||||
if(text)
|
||||
{
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "%d", no);
|
||||
ui_do_label(&no_area, buf, bars.h*0.75f, 0);
|
||||
}
|
||||
|
||||
pass_area.w -= no_area.w;
|
||||
}
|
||||
|
||||
if(text && pass)
|
||||
{
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "%d", pass);
|
||||
ui_do_label(&pass_area, buf, bars.h*0.75f, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <game/client/component.hpp>
|
||||
#include <game/client/ui.hpp>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <engine/e_memheap.h>
|
||||
}
|
||||
|
||||
class VOTING : public COMPONENT
|
||||
{
|
||||
HEAP *heap;
|
||||
|
||||
static void con_callvote(void *result, void *user_data);
|
||||
static void con_vote(void *result, void *user_data);
|
||||
|
||||
int64 closetime;
|
||||
char description[512];
|
||||
char command[512];
|
||||
int voted;
|
||||
|
||||
void clearoptions();
|
||||
void callvote(const char *type, const char *value);
|
||||
|
||||
public:
|
||||
|
||||
struct VOTEOPTION
|
||||
{
|
||||
VOTEOPTION *next;
|
||||
VOTEOPTION *prev;
|
||||
char command[1];
|
||||
};
|
||||
VOTEOPTION *first;
|
||||
VOTEOPTION *last;
|
||||
|
||||
VOTING();
|
||||
virtual void on_reset();
|
||||
virtual void on_console_init();
|
||||
virtual void on_message(int msgtype, void *rawmsg);
|
||||
virtual void on_render();
|
||||
|
||||
void render_bars(RECT bars, bool text);
|
||||
|
||||
void callvote_kick(int client_id);
|
||||
void callvote_option(int option);
|
||||
|
||||
void vote(int v); // -1 = no, 1 = yes
|
||||
|
||||
int seconds_left() { return (closetime - time_get())/time_freq(); }
|
||||
bool is_voting() { return closetime != 0; }
|
||||
int taken_choice() const { return voted; }
|
||||
const char *vote_description() const { return description; }
|
||||
const char *vote_command() const { return command; }
|
||||
|
||||
int yes, no, pass, total;
|
||||
};
|
||||
|
||||
885
project/jni/application/teeworlds/src/game/client/gameclient.cpp
Normal file
885
project/jni/application/teeworlds/src/game/client/gameclient.cpp
Normal file
@@ -0,0 +1,885 @@
|
||||
#include <string.h>
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_demorec.h>
|
||||
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
|
||||
#include <game/layers.hpp>
|
||||
#include "render.hpp"
|
||||
|
||||
#include "gameclient.hpp"
|
||||
|
||||
#include "components/binds.hpp"
|
||||
#include "components/broadcast.hpp"
|
||||
#include "components/camera.hpp"
|
||||
#include "components/chat.hpp"
|
||||
#include "components/console.hpp"
|
||||
#include "components/controls.hpp"
|
||||
#include "components/damageind.hpp"
|
||||
#include "components/debughud.hpp"
|
||||
#include "components/effects.hpp"
|
||||
#include "components/emoticon.hpp"
|
||||
#include "components/flow.hpp"
|
||||
#include "components/hud.hpp"
|
||||
#include "components/items.hpp"
|
||||
#include "components/killmessages.hpp"
|
||||
#include "components/mapimages.hpp"
|
||||
#include "components/maplayers.hpp"
|
||||
#include "components/menus.hpp"
|
||||
#include "components/motd.hpp"
|
||||
#include "components/particles.hpp"
|
||||
#include "components/players.hpp"
|
||||
#include "components/nameplates.hpp"
|
||||
#include "components/scoreboard.hpp"
|
||||
#include "components/skins.hpp"
|
||||
#include "components/sounds.hpp"
|
||||
#include "components/voting.hpp"
|
||||
|
||||
GAMECLIENT gameclient;
|
||||
|
||||
// instanciate all systems
|
||||
static KILLMESSAGES killmessages;
|
||||
static CAMERA camera;
|
||||
static CHAT chat;
|
||||
static MOTD motd;
|
||||
static BROADCAST broadcast;
|
||||
static CONSOLE console;
|
||||
static BINDS binds;
|
||||
static PARTICLES particles;
|
||||
static MENUS menus;
|
||||
static SKINS skins;
|
||||
static FLOW flow;
|
||||
static HUD hud;
|
||||
static DEBUGHUD debughud;
|
||||
static CONTROLS controls;
|
||||
static EFFECTS effects;
|
||||
static SCOREBOARD scoreboard;
|
||||
static SOUNDS sounds;
|
||||
static EMOTICON emoticon;
|
||||
static DAMAGEIND damageind;
|
||||
static VOTING voting;
|
||||
|
||||
static PLAYERS players;
|
||||
static NAMEPLATES nameplates;
|
||||
static ITEMS items;
|
||||
static MAPIMAGES mapimages;
|
||||
|
||||
static MAPLAYERS maplayers_background(MAPLAYERS::TYPE_BACKGROUND);
|
||||
static MAPLAYERS maplayers_foreground(MAPLAYERS::TYPE_FOREGROUND);
|
||||
|
||||
GAMECLIENT::STACK::STACK() { num = 0; }
|
||||
void GAMECLIENT::STACK::add(class COMPONENT *component) { components[num++] = component; }
|
||||
|
||||
static int load_current;
|
||||
static int load_total;
|
||||
|
||||
static void load_sounds_thread(void *do_render)
|
||||
{
|
||||
// load sounds
|
||||
for(int s = 0; s < data->num_sounds; s++)
|
||||
{
|
||||
if(do_render)
|
||||
gameclient.menus->render_loading(load_current/(float)load_total);
|
||||
for(int i = 0; i < data->sounds[s].num_sounds; i++)
|
||||
{
|
||||
int id = snd_load_wv(data->sounds[s].sounds[i].filename);
|
||||
data->sounds[s].sounds[i].id = id;
|
||||
}
|
||||
|
||||
if(do_render)
|
||||
load_current++;
|
||||
}
|
||||
}
|
||||
|
||||
static void con_serverdummy(void *result, void *user_data)
|
||||
{
|
||||
dbg_msg("client", "this command is not available on the client");
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_console_init()
|
||||
{
|
||||
// setup pointers
|
||||
binds = &::binds;
|
||||
console = &::console;
|
||||
particles = &::particles;
|
||||
menus = &::menus;
|
||||
skins = &::skins;
|
||||
chat = &::chat;
|
||||
flow = &::flow;
|
||||
camera = &::camera;
|
||||
controls = &::controls;
|
||||
effects = &::effects;
|
||||
sounds = &::sounds;
|
||||
motd = &::motd;
|
||||
damageind = &::damageind;
|
||||
mapimages = &::mapimages;
|
||||
voting = &::voting;
|
||||
|
||||
// make a list of all the systems, make sure to add them in the corrent render order
|
||||
all.add(skins);
|
||||
all.add(mapimages);
|
||||
all.add(effects); // doesn't render anything, just updates effects
|
||||
all.add(particles);
|
||||
all.add(binds);
|
||||
all.add(controls);
|
||||
all.add(camera);
|
||||
all.add(sounds);
|
||||
all.add(voting);
|
||||
all.add(particles); // doesn't render anything, just updates all the particles
|
||||
|
||||
all.add(&maplayers_background); // first to render
|
||||
all.add(&particles->render_trail);
|
||||
all.add(&particles->render_explosions);
|
||||
all.add(&items);
|
||||
all.add(&players);
|
||||
all.add(&maplayers_foreground);
|
||||
all.add(&nameplates);
|
||||
all.add(&particles->render_general);
|
||||
all.add(damageind);
|
||||
all.add(&hud);
|
||||
all.add(&emoticon);
|
||||
all.add(&killmessages);
|
||||
all.add(chat);
|
||||
all.add(&broadcast);
|
||||
all.add(&debughud);
|
||||
all.add(&scoreboard);
|
||||
all.add(motd);
|
||||
all.add(menus);
|
||||
all.add(console);
|
||||
|
||||
// build the input stack
|
||||
input.add(&menus->binder); // this will take over all input when we want to bind a key
|
||||
input.add(&binds->special_binds);
|
||||
input.add(console);
|
||||
input.add(chat); // chat has higher prio due to tha you can quit it by pressing esc
|
||||
input.add(motd); // for pressing esc to remove it
|
||||
input.add(menus);
|
||||
input.add(&emoticon);
|
||||
input.add(controls);
|
||||
input.add(binds);
|
||||
|
||||
// add the some console commands
|
||||
MACRO_REGISTER_COMMAND("team", "i", CFGFLAG_CLIENT, con_team, this, "Switch team");
|
||||
MACRO_REGISTER_COMMAND("kill", "", CFGFLAG_CLIENT, con_kill, this, "Kill yourself");
|
||||
|
||||
// register server dummy commands for tab completion
|
||||
MACRO_REGISTER_COMMAND("tune", "si", CFGFLAG_SERVER, con_serverdummy, 0, "Tune variable to value");
|
||||
MACRO_REGISTER_COMMAND("tune_reset", "", CFGFLAG_SERVER, con_serverdummy, 0, "Reset tuning");
|
||||
MACRO_REGISTER_COMMAND("tune_dump", "", CFGFLAG_SERVER, con_serverdummy, 0, "Dump tuning");
|
||||
MACRO_REGISTER_COMMAND("change_map", "r", CFGFLAG_SERVER, con_serverdummy, 0, "Change map");
|
||||
MACRO_REGISTER_COMMAND("restart", "?i", CFGFLAG_SERVER, con_serverdummy, 0, "Restart in x seconds");
|
||||
MACRO_REGISTER_COMMAND("broadcast", "r", CFGFLAG_SERVER, con_serverdummy, 0, "Broadcast message");
|
||||
/*MACRO_REGISTER_COMMAND("say", "r", CFGFLAG_SERVER, con_serverdummy, 0);*/
|
||||
MACRO_REGISTER_COMMAND("set_team", "ii", CFGFLAG_SERVER, con_serverdummy, 0, "Set team of player to team");
|
||||
MACRO_REGISTER_COMMAND("addvote", "r", CFGFLAG_SERVER, con_serverdummy, 0, "Add a voting option");
|
||||
/*MACRO_REGISTER_COMMAND("vote", "", CFGFLAG_SERVER, con_serverdummy, 0);*/
|
||||
|
||||
// let all the other components register their console commands
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_console_init();
|
||||
|
||||
//
|
||||
suppress_events = false;
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_init()
|
||||
{
|
||||
// init all components
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_init();
|
||||
|
||||
// setup item sizes
|
||||
for(int i = 0; i < NUM_NETOBJTYPES; i++)
|
||||
snap_set_staticsize(i, netobj_get_size(i));
|
||||
|
||||
// load default font
|
||||
static FONT_SET default_font;
|
||||
int64 start = time_get();
|
||||
|
||||
int before = gfx_memory_usage();
|
||||
font_set_load(&default_font, "fonts/default_font%d.tfnt", "fonts/default_font%d.png", "fonts/default_font%d_b.png", 14, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 36);
|
||||
dbg_msg("font", "gfx memory used for font textures: %d", gfx_memory_usage()-before);
|
||||
|
||||
gfx_text_set_default_font(&default_font);
|
||||
|
||||
config.cl_threadsoundloading = 0;
|
||||
|
||||
// setup load amount
|
||||
load_total = data->num_images;
|
||||
load_current = 0;
|
||||
if(!config.cl_threadsoundloading)
|
||||
load_total += data->num_sounds;
|
||||
|
||||
// load textures
|
||||
for(int i = 0; i < data->num_images; i++)
|
||||
{
|
||||
gameclient.menus->render_loading(load_current/load_total);
|
||||
data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO, 0);
|
||||
load_current++;
|
||||
}
|
||||
|
||||
::skins.init();
|
||||
|
||||
if(config.cl_threadsoundloading)
|
||||
thread_create(load_sounds_thread, 0);
|
||||
else
|
||||
load_sounds_thread((void*)1);
|
||||
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_reset();
|
||||
|
||||
int64 end = time_get();
|
||||
dbg_msg("", "%f.2ms", ((end-start)*1000)/(float)time_freq());
|
||||
|
||||
servermode = SERVERMODE_PURE;
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_save()
|
||||
{
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_save();
|
||||
}
|
||||
|
||||
void GAMECLIENT::dispatch_input()
|
||||
{
|
||||
// handle mouse movement
|
||||
int x=0, y=0;
|
||||
inp_mouse_relative(&x, &y);
|
||||
if(x || y)
|
||||
{
|
||||
for(int h = 0; h < input.num; h++)
|
||||
{
|
||||
if(input.components[h]->on_mousemove(x, y))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// handle key presses
|
||||
for(int i = 0; i < inp_num_events(); i++)
|
||||
{
|
||||
INPUT_EVENT e = inp_get_event(i);
|
||||
|
||||
for(int h = 0; h < input.num; h++)
|
||||
{
|
||||
if(input.components[h]->on_input(e))
|
||||
{
|
||||
//dbg_msg("", "%d char=%d key=%d flags=%d", h, e.ch, e.key, e.flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clear all events for this frame
|
||||
inp_clear_events();
|
||||
}
|
||||
|
||||
|
||||
int GAMECLIENT::on_snapinput(int *data)
|
||||
{
|
||||
return controls->snapinput(data);
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_connected()
|
||||
{
|
||||
layers_init();
|
||||
col_init();
|
||||
render_tilemap_generate_skip();
|
||||
|
||||
for(int i = 0; i < all.num; i++)
|
||||
{
|
||||
all.components[i]->on_mapload();
|
||||
all.components[i]->on_reset();
|
||||
}
|
||||
|
||||
SERVER_INFO current_server_info;
|
||||
client_serverinfo(¤t_server_info);
|
||||
|
||||
servermode = SERVERMODE_PURE;
|
||||
|
||||
// send the inital info
|
||||
send_info(true);
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_reset()
|
||||
{
|
||||
// clear out the invalid pointers
|
||||
last_new_predicted_tick = -1;
|
||||
mem_zero(&gameclient.snap, sizeof(gameclient.snap));
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
clients[i].name[0] = 0;
|
||||
clients[i].skin_id = 0;
|
||||
clients[i].team = 0;
|
||||
clients[i].angle = 0;
|
||||
clients[i].emoticon = 0;
|
||||
clients[i].emoticon_start = -1;
|
||||
clients[i].skin_info.texture = gameclient.skins->get(0)->color_texture;
|
||||
clients[i].skin_info.color_body = vec4(1,1,1,1);
|
||||
clients[i].skin_info.color_feet = vec4(1,1,1,1);
|
||||
clients[i].update_render_info();
|
||||
}
|
||||
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_reset();
|
||||
}
|
||||
|
||||
|
||||
void GAMECLIENT::update_local_character_pos()
|
||||
{
|
||||
if(config.cl_predict && client_state() != CLIENTSTATE_DEMOPLAYBACK)
|
||||
{
|
||||
if(!snap.local_character || (snap.local_character->health < 0) || (snap.gameobj && snap.gameobj->game_over))
|
||||
{
|
||||
// don't use predicted
|
||||
}
|
||||
else
|
||||
local_character_pos = mix(predicted_prev_char.pos, predicted_char.pos, client_predintratick());
|
||||
}
|
||||
else if(snap.local_character && snap.local_prev_character)
|
||||
{
|
||||
local_character_pos = mix(
|
||||
vec2(snap.local_prev_character->x, snap.local_prev_character->y),
|
||||
vec2(snap.local_character->x, snap.local_character->y), client_intratick());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
while(character->tick < tick)
|
||||
{
|
||||
character->tick++;
|
||||
tempcore.tick(false);
|
||||
tempcore.move();
|
||||
tempcore.quantize();
|
||||
}
|
||||
|
||||
tempcore.write(character);
|
||||
}
|
||||
|
||||
|
||||
void GAMECLIENT::on_render()
|
||||
{
|
||||
// update the local character position
|
||||
update_local_character_pos();
|
||||
|
||||
// dispatch all input to systems
|
||||
dispatch_input();
|
||||
|
||||
// render all systems
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_render();
|
||||
|
||||
// clear new tick flags
|
||||
new_tick = false;
|
||||
new_predicted_tick = false;
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_message(int msgtype)
|
||||
{
|
||||
|
||||
// special messages
|
||||
if(msgtype == NETMSGTYPE_SV_EXTRAPROJECTILE)
|
||||
{
|
||||
/*
|
||||
int num = msg_unpack_int();
|
||||
|
||||
for(int k = 0; k < num; k++)
|
||||
{
|
||||
NETOBJ_PROJECTILE proj;
|
||||
for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
|
||||
((int *)&proj)[i] = msg_unpack_int();
|
||||
|
||||
if(msg_unpack_error())
|
||||
return;
|
||||
|
||||
if(extraproj_num != MAX_EXTRA_PROJECTILES)
|
||||
{
|
||||
extraproj_projectiles[extraproj_num] = proj;
|
||||
extraproj_num++;
|
||||
}
|
||||
}
|
||||
|
||||
return;*/
|
||||
}
|
||||
else if(msgtype == NETMSGTYPE_SV_TUNEPARAMS)
|
||||
{
|
||||
// unpack the new tuning
|
||||
TUNING_PARAMS new_tuning;
|
||||
int *params = (int *)&new_tuning;
|
||||
for(unsigned i = 0; i < sizeof(TUNING_PARAMS)/sizeof(int); i++)
|
||||
params[i] = msg_unpack_int();
|
||||
|
||||
// check for unpacking errors
|
||||
if(msg_unpack_error())
|
||||
return;
|
||||
|
||||
servermode = SERVERMODE_PURE;
|
||||
|
||||
// apply new tuning
|
||||
tuning = new_tuning;
|
||||
return;
|
||||
}
|
||||
|
||||
void *rawmsg = netmsg_secure_unpack(msgtype);
|
||||
if(!rawmsg)
|
||||
{
|
||||
dbg_msg("client", "dropped weird message '%s' (%d), failed on '%s'", netmsg_get_name(msgtype), msgtype, netmsg_failed_on());
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: this should be done smarter
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_message(msgtype, rawmsg);
|
||||
|
||||
if(msgtype == NETMSGTYPE_SV_READYTOENTER)
|
||||
{
|
||||
client_entergame();
|
||||
}
|
||||
else if (msgtype == NETMSGTYPE_SV_EMOTICON)
|
||||
{
|
||||
NETMSG_SV_EMOTICON *msg = (NETMSG_SV_EMOTICON *)rawmsg;
|
||||
|
||||
// apply
|
||||
clients[msg->cid].emoticon = msg->emoticon;
|
||||
clients[msg->cid].emoticon_start = client_tick();
|
||||
}
|
||||
else if(msgtype == NETMSGTYPE_SV_SOUNDGLOBAL)
|
||||
{
|
||||
if(suppress_events)
|
||||
return;
|
||||
|
||||
NETMSG_SV_SOUNDGLOBAL *msg = (NETMSG_SV_SOUNDGLOBAL *)rawmsg;
|
||||
gameclient.sounds->play(SOUNDS::CHN_GLOBAL, msg->soundid, 1.0f, vec2(0,0));
|
||||
}
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_statechange(int new_state, int old_state)
|
||||
{
|
||||
if(demorec_isrecording())
|
||||
demorec_record_stop();
|
||||
|
||||
// reset everything
|
||||
on_reset();
|
||||
|
||||
// then change the state
|
||||
for(int i = 0; i < all.num; i++)
|
||||
all.components[i]->on_statechange(new_state, old_state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GAMECLIENT::process_events()
|
||||
{
|
||||
if(suppress_events)
|
||||
return;
|
||||
|
||||
int snaptype = SNAP_CURRENT;
|
||||
int num = snap_num_items(snaptype);
|
||||
for(int index = 0; index < num; index++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(snaptype, index, &item);
|
||||
|
||||
if(item.type == NETEVENTTYPE_DAMAGEIND)
|
||||
{
|
||||
NETEVENT_DAMAGEIND *ev = (NETEVENT_DAMAGEIND *)data;
|
||||
gameclient.effects->damage_indicator(vec2(ev->x, ev->y), get_direction(ev->angle));
|
||||
}
|
||||
else if(item.type == NETEVENTTYPE_EXPLOSION)
|
||||
{
|
||||
NETEVENT_EXPLOSION *ev = (NETEVENT_EXPLOSION *)data;
|
||||
gameclient.effects->explosion(vec2(ev->x, ev->y));
|
||||
}
|
||||
else if(item.type == NETEVENTTYPE_HAMMERHIT)
|
||||
{
|
||||
NETEVENT_HAMMERHIT *ev = (NETEVENT_HAMMERHIT *)data;
|
||||
gameclient.effects->hammerhit(vec2(ev->x, ev->y));
|
||||
}
|
||||
else if(item.type == NETEVENTTYPE_SPAWN)
|
||||
{
|
||||
NETEVENT_SPAWN *ev = (NETEVENT_SPAWN *)data;
|
||||
gameclient.effects->playerspawn(vec2(ev->x, ev->y));
|
||||
}
|
||||
else if(item.type == NETEVENTTYPE_DEATH)
|
||||
{
|
||||
NETEVENT_DEATH *ev = (NETEVENT_DEATH *)data;
|
||||
gameclient.effects->playerdeath(vec2(ev->x, ev->y), ev->cid);
|
||||
}
|
||||
else if(item.type == NETEVENTTYPE_SOUNDWORLD)
|
||||
{
|
||||
NETEVENT_SOUNDWORLD *ev = (NETEVENT_SOUNDWORLD *)data;
|
||||
gameclient.sounds->play(SOUNDS::CHN_WORLD, ev->soundid, 1.0f, vec2(ev->x, ev->y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GAMECLIENT::on_snapshot()
|
||||
{
|
||||
new_tick = true;
|
||||
|
||||
// clear out the invalid pointers
|
||||
mem_zero(&gameclient.snap, sizeof(gameclient.snap));
|
||||
snap.local_cid = -1;
|
||||
|
||||
// secure snapshot
|
||||
{
|
||||
int num = snap_num_items(SNAP_CURRENT);
|
||||
for(int index = 0; index < num; index++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
void *data = snap_get_item(SNAP_CURRENT, index, &item);
|
||||
if(netobj_validate(item.type, data, item.datasize) != 0)
|
||||
{
|
||||
if(config.debug)
|
||||
dbg_msg("game", "invalidated index=%d type=%d (%s) size=%d id=%d", index, item.type, netobj_get_name(item.type), item.datasize, item.id);
|
||||
snap_invalidate_item(SNAP_CURRENT, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process_events();
|
||||
|
||||
if(config.dbg_stress)
|
||||
{
|
||||
if((client_tick()%100) == 0)
|
||||
{
|
||||
char message[64];
|
||||
int msglen = rand()%(sizeof(message)-1);
|
||||
for(int i = 0; i < msglen; i++)
|
||||
message[i] = 'a'+(rand()%('z'-'a'));
|
||||
message[msglen] = 0;
|
||||
|
||||
NETMSG_CL_SAY msg;
|
||||
msg.team = rand()&1;
|
||||
msg.message = message;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
}
|
||||
|
||||
// go trough all the items in the snapshot and gather the info we want
|
||||
{
|
||||
snap.team_size[0] = snap.team_size[1] = 0;
|
||||
|
||||
int num = snap_num_items(SNAP_CURRENT);
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
|
||||
if(item.type == NETOBJTYPE_CLIENT_INFO)
|
||||
{
|
||||
const NETOBJ_CLIENT_INFO *info = (const NETOBJ_CLIENT_INFO *)data;
|
||||
int cid = item.id;
|
||||
ints_to_str(&info->name0, 6, clients[cid].name);
|
||||
ints_to_str(&info->skin0, 6, clients[cid].skin_name);
|
||||
|
||||
clients[cid].use_custom_color = info->use_custom_color;
|
||||
clients[cid].color_body = info->color_body;
|
||||
clients[cid].color_feet = info->color_feet;
|
||||
|
||||
// prepare the info
|
||||
if(clients[cid].skin_name[0] == 'x' || clients[cid].skin_name[1] == '_')
|
||||
str_copy(clients[cid].skin_name, "default", 64);
|
||||
|
||||
clients[cid].skin_info.color_body = skins->get_color(clients[cid].color_body);
|
||||
clients[cid].skin_info.color_feet = skins->get_color(clients[cid].color_feet);
|
||||
clients[cid].skin_info.size = 64;
|
||||
|
||||
// find new skin
|
||||
clients[cid].skin_id = gameclient.skins->find(clients[cid].skin_name);
|
||||
if(clients[cid].skin_id < 0)
|
||||
{
|
||||
clients[cid].skin_id = gameclient.skins->find("default");
|
||||
if(clients[cid].skin_id < 0)
|
||||
clients[cid].skin_id = 0;
|
||||
}
|
||||
|
||||
if(clients[cid].use_custom_color)
|
||||
clients[cid].skin_info.texture = gameclient.skins->get(clients[cid].skin_id)->color_texture;
|
||||
else
|
||||
{
|
||||
clients[cid].skin_info.texture = gameclient.skins->get(clients[cid].skin_id)->org_texture;
|
||||
clients[cid].skin_info.color_body = vec4(1,1,1,1);
|
||||
clients[cid].skin_info.color_feet = vec4(1,1,1,1);
|
||||
}
|
||||
|
||||
clients[cid].update_render_info();
|
||||
gameclient.snap.num_players++;
|
||||
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_PLAYER_INFO)
|
||||
{
|
||||
const NETOBJ_PLAYER_INFO *info = (const NETOBJ_PLAYER_INFO *)data;
|
||||
|
||||
clients[info->cid].team = info->team;
|
||||
snap.player_infos[info->cid] = info;
|
||||
|
||||
if(info->local)
|
||||
{
|
||||
snap.local_cid = item.id;
|
||||
snap.local_info = info;
|
||||
|
||||
if (info->team == -1)
|
||||
snap.spectate = true;
|
||||
}
|
||||
|
||||
// calculate team-balance
|
||||
if(info->team != -1)
|
||||
snap.team_size[info->team]++;
|
||||
|
||||
}
|
||||
else if(item.type == NETOBJTYPE_CHARACTER)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
snap.spectate = true;
|
||||
|
||||
TUNING_PARAMS standard_tuning;
|
||||
SERVER_INFO current_server_info;
|
||||
client_serverinfo(¤t_server_info);
|
||||
if(current_server_info.gametype[0] != '0')
|
||||
{
|
||||
if(strcmp(current_server_info.gametype, "DM") != 0 && strcmp(current_server_info.gametype, "TDM") != 0 && strcmp(current_server_info.gametype, "CTF") != 0)
|
||||
servermode = SERVERMODE_MOD;
|
||||
else if(memcmp(&standard_tuning, &tuning, sizeof(TUNING_PARAMS)) == 0)
|
||||
servermode = SERVERMODE_PURE;
|
||||
else
|
||||
servermode = SERVERMODE_PUREMOD;
|
||||
}
|
||||
|
||||
|
||||
// update render info
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
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;
|
||||
|
||||
// don't predict anything if we are paused
|
||||
if(snap.gameobj && snap.gameobj->paused)
|
||||
{
|
||||
if(snap.local_character)
|
||||
predicted_char.read(snap.local_character);
|
||||
if(snap.local_prev_character)
|
||||
predicted_prev_char.read(snap.local_prev_character);
|
||||
return;
|
||||
}
|
||||
|
||||
// repredict character
|
||||
WORLD_CORE world;
|
||||
world.tuning = tuning;
|
||||
|
||||
// search for players
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// predict
|
||||
for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
|
||||
{
|
||||
// fetch the local
|
||||
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++)
|
||||
{
|
||||
if(!world.characters[c])
|
||||
continue;
|
||||
|
||||
mem_zero(&world.characters[c]->input, sizeof(world.characters[c]->input));
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
// move all players and quantize their data
|
||||
for(int c = 0; c < MAX_CLIENTS; c++)
|
||||
{
|
||||
if(!world.characters[c])
|
||||
continue;
|
||||
|
||||
world.characters[c]->move();
|
||||
world.characters[c]->quantize();
|
||||
}
|
||||
|
||||
// check if we want to trigger effects
|
||||
if(tick > last_new_predicted_tick)
|
||||
{
|
||||
last_new_predicted_tick = tick;
|
||||
new_predicted_tick = true;
|
||||
|
||||
if(snap.local_cid != -1 && world.characters[snap.local_cid])
|
||||
{
|
||||
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_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
|
||||
|
||||
/*if(events&COREEVENT_AIR_JUMP)
|
||||
{
|
||||
gameclient.effects->air_jump(pos);
|
||||
gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos);
|
||||
}*/
|
||||
|
||||
//if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
|
||||
//if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos);
|
||||
if(events&COREEVENT_HOOK_ATTACH_GROUND) gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos);
|
||||
if(events&COREEVENT_HOOK_HIT_NOHOOK) gameclient.sounds->play_and_record(SOUNDS::CHN_WORLD, SOUND_HOOK_NOATTACH, 1.0f, pos);
|
||||
//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
|
||||
}
|
||||
}
|
||||
|
||||
if(tick == client_predtick() && world.characters[snap.local_cid])
|
||||
predicted_char = *world.characters[snap.local_cid];
|
||||
}
|
||||
|
||||
if(config.debug && config.cl_predict && predicted_tick == client_predtick())
|
||||
{
|
||||
NETOBJ_CHARACTER_CORE before = {0}, now = {0}, before_prev = {0}, now_prev = {0};
|
||||
before_char.write(&before);
|
||||
before_prev_char.write(&before_prev);
|
||||
predicted_char.write(&now);
|
||||
predicted_prev_char.write(&now_prev);
|
||||
|
||||
if(mem_comp(&before, &now, sizeof(NETOBJ_CHARACTER_CORE)) != 0)
|
||||
{
|
||||
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 (%d %d)", i, ((int *)&before)[i], ((int *)&now)[i], ((int *)&before_prev)[i], ((int *)&now_prev)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
predicted_tick = client_predtick();
|
||||
}
|
||||
|
||||
void GAMECLIENT::CLIENT_DATA::update_render_info()
|
||||
{
|
||||
render_info = skin_info;
|
||||
|
||||
// force team colors
|
||||
if(gameclient.snap.gameobj && gameclient.snap.gameobj->flags&GAMEFLAG_TEAMS)
|
||||
{
|
||||
const int team_colors[2] = {65387, 10223467};
|
||||
if(team >= 0 || team <= 1)
|
||||
{
|
||||
render_info.texture = gameclient.skins->get(skin_id)->color_texture;
|
||||
render_info.color_body = gameclient.skins->get_color(team_colors[team]);
|
||||
render_info.color_feet = gameclient.skins->get_color(team_colors[team]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GAMECLIENT::send_switch_team(int team)
|
||||
{
|
||||
NETMSG_CL_SETTEAM msg;
|
||||
msg.team = team;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
|
||||
void GAMECLIENT::send_info(bool start)
|
||||
{
|
||||
if(start)
|
||||
{
|
||||
NETMSG_CL_STARTINFO msg;
|
||||
msg.name = config.player_name;
|
||||
msg.skin = config.player_skin;
|
||||
msg.use_custom_color = config.player_use_custom_color;
|
||||
msg.color_body = config.player_color_body;
|
||||
msg.color_feet = config.player_color_feet;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
NETMSG_CL_CHANGEINFO msg;
|
||||
msg.name = config.player_name;
|
||||
msg.skin = config.player_skin;
|
||||
msg.use_custom_color = config.player_use_custom_color;
|
||||
msg.color_body = config.player_color_body;
|
||||
msg.color_feet = config.player_color_feet;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
}
|
||||
client_send_msg();
|
||||
}
|
||||
|
||||
void GAMECLIENT::send_kill(int client_id)
|
||||
{
|
||||
NETMSG_CL_KILL msg;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
client_send_msg();
|
||||
}
|
||||
|
||||
void GAMECLIENT::con_team(void *result, void *user_data)
|
||||
{
|
||||
((GAMECLIENT*)user_data)->send_switch_team(console_arg_int(result, 0));
|
||||
}
|
||||
|
||||
void GAMECLIENT::con_kill(void *result, void *user_data)
|
||||
{
|
||||
((GAMECLIENT*)user_data)->send_kill(-1);
|
||||
}
|
||||
159
project/jni/application/teeworlds/src/game/client/gameclient.hpp
Normal file
159
project/jni/application/teeworlds/src/game/client/gameclient.hpp
Normal file
@@ -0,0 +1,159 @@
|
||||
|
||||
#include <base/vmath.hpp>
|
||||
#include <game/gamecore.hpp>
|
||||
#include "render.hpp"
|
||||
|
||||
class GAMECLIENT
|
||||
{
|
||||
class STACK
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
MAX_COMPONENTS = 64,
|
||||
};
|
||||
|
||||
STACK();
|
||||
void add(class COMPONENT *component);
|
||||
|
||||
class COMPONENT *components[MAX_COMPONENTS];
|
||||
int num;
|
||||
};
|
||||
|
||||
STACK all;
|
||||
STACK input;
|
||||
|
||||
|
||||
void dispatch_input();
|
||||
void process_events();
|
||||
void update_local_character_pos();
|
||||
|
||||
int predicted_tick;
|
||||
int last_new_predicted_tick;
|
||||
|
||||
static void con_team(void *result, void *user_data);
|
||||
static void con_kill(void *result, void *user_data);
|
||||
|
||||
public:
|
||||
bool suppress_events;
|
||||
bool new_tick;
|
||||
bool new_predicted_tick;
|
||||
|
||||
// TODO: move this
|
||||
TUNING_PARAMS tuning;
|
||||
|
||||
enum
|
||||
{
|
||||
SERVERMODE_PURE=0,
|
||||
SERVERMODE_MOD,
|
||||
SERVERMODE_PUREMOD,
|
||||
};
|
||||
int servermode;
|
||||
|
||||
vec2 local_character_pos;
|
||||
|
||||
// predicted players
|
||||
CHARACTER_CORE predicted_prev_char;
|
||||
CHARACTER_CORE predicted_char;
|
||||
|
||||
// snap pointers
|
||||
struct SNAPSTATE
|
||||
{
|
||||
const NETOBJ_CHARACTER *local_character;
|
||||
const NETOBJ_CHARACTER *local_prev_character;
|
||||
const NETOBJ_PLAYER_INFO *local_info;
|
||||
const NETOBJ_FLAG *flags[2];
|
||||
const NETOBJ_GAME *gameobj;
|
||||
|
||||
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;
|
||||
|
||||
// client data
|
||||
struct CLIENT_DATA
|
||||
{
|
||||
int use_custom_color;
|
||||
int color_body;
|
||||
int color_feet;
|
||||
|
||||
char name[64];
|
||||
char skin_name[64];
|
||||
int skin_id;
|
||||
int skin_color;
|
||||
int team;
|
||||
int emoticon;
|
||||
int emoticon_start;
|
||||
CHARACTER_CORE predicted;
|
||||
|
||||
TEE_RENDER_INFO skin_info; // this is what the server reports
|
||||
TEE_RENDER_INFO render_info; // this is what we use
|
||||
|
||||
float angle;
|
||||
|
||||
void update_render_info();
|
||||
};
|
||||
|
||||
CLIENT_DATA clients[MAX_CLIENTS];
|
||||
|
||||
void on_reset();
|
||||
|
||||
// hooks
|
||||
void on_connected();
|
||||
void on_render();
|
||||
void on_init();
|
||||
void on_save();
|
||||
void on_console_init();
|
||||
void on_statechange(int new_state, int old_state);
|
||||
void on_message(int msgtype);
|
||||
void on_snapshot();
|
||||
void on_predict();
|
||||
int on_snapinput(int *data);
|
||||
|
||||
// actions
|
||||
// TODO: move these
|
||||
void send_switch_team(int team);
|
||||
void send_info(bool start);
|
||||
void send_kill(int client_id);
|
||||
|
||||
// pointers to all systems
|
||||
class CONSOLE *console;
|
||||
class BINDS *binds;
|
||||
class PARTICLES *particles;
|
||||
class MENUS *menus;
|
||||
class SKINS *skins;
|
||||
class FLOW *flow;
|
||||
class CHAT *chat;
|
||||
class DAMAGEIND *damageind;
|
||||
class CAMERA *camera;
|
||||
class CONTROLS *controls;
|
||||
class EFFECTS *effects;
|
||||
class SOUNDS *sounds;
|
||||
class MOTD *motd;
|
||||
class MAPIMAGES *mapimages;
|
||||
class VOTING *voting;
|
||||
};
|
||||
|
||||
extern GAMECLIENT gameclient;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <string.h> // strlen
|
||||
#include "lineinput.hpp"
|
||||
|
||||
LINEINPUT::LINEINPUT()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void LINEINPUT::clear()
|
||||
{
|
||||
mem_zero(str, sizeof(str));
|
||||
len = 0;
|
||||
cursor_pos = 0;
|
||||
}
|
||||
|
||||
void LINEINPUT::set(const char *string)
|
||||
{
|
||||
str_copy(str, string, sizeof(str));
|
||||
len = strlen(str);
|
||||
cursor_pos = len;
|
||||
}
|
||||
|
||||
void LINEINPUT::process_input(INPUT_EVENT e)
|
||||
{
|
||||
if(cursor_pos > len)
|
||||
cursor_pos = len;
|
||||
|
||||
char c = e.ch;
|
||||
int k = e.key;
|
||||
|
||||
// 127 is produced on Mac OS X and corresponds to the delete key
|
||||
if (!(c >= 0 && c < 32) && c != 127)
|
||||
{
|
||||
if (len < sizeof(str) - 1 && cursor_pos < sizeof(str) - 1)
|
||||
{
|
||||
memmove(str + cursor_pos + 1, str + cursor_pos, len - cursor_pos + 1);
|
||||
str[cursor_pos] = c;
|
||||
cursor_pos++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
if(e.flags&INPFLAG_PRESS)
|
||||
{
|
||||
if (k == KEY_BACKSPACE && cursor_pos > 0)
|
||||
{
|
||||
memmove(str + cursor_pos - 1, str + cursor_pos, len - cursor_pos + 1);
|
||||
cursor_pos--;
|
||||
len--;
|
||||
}
|
||||
else if (k == KEY_DELETE && cursor_pos < len)
|
||||
{
|
||||
memmove(str + cursor_pos, str + cursor_pos + 1, len - cursor_pos);
|
||||
len--;
|
||||
}
|
||||
else if (k == KEY_LEFT && cursor_pos > 0)
|
||||
cursor_pos--;
|
||||
else if (k == KEY_RIGHT && cursor_pos < len)
|
||||
cursor_pos++;
|
||||
else if (k == KEY_HOME)
|
||||
cursor_pos = 0;
|
||||
else if (k == KEY_END)
|
||||
cursor_pos = len;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef GAME_CLIENT_LINEINPUT_H
|
||||
#define GAME_CLIENT_LINEINPUT_H
|
||||
|
||||
// line input helter
|
||||
class LINEINPUT
|
||||
{
|
||||
char str[256];
|
||||
unsigned len;
|
||||
unsigned cursor_pos;
|
||||
public:
|
||||
class CALLBACK
|
||||
{
|
||||
public:
|
||||
virtual ~CALLBACK() {}
|
||||
virtual bool event(INPUT_EVENT e) = 0;
|
||||
};
|
||||
|
||||
LINEINPUT();
|
||||
void clear();
|
||||
void process_input(INPUT_EVENT e);
|
||||
void set(const char *string);
|
||||
const char *get_string() const { return str; }
|
||||
int get_length() const { return len; }
|
||||
unsigned cursor_offset() const { return cursor_pos; }
|
||||
};
|
||||
|
||||
#endif
|
||||
311
project/jni/application/teeworlds/src/game/client/render.cpp
Normal file
311
project/jni/application/teeworlds/src/game/client/render.cpp
Normal file
@@ -0,0 +1,311 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <math.h>
|
||||
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <game/generated/gc_data.hpp>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/layers.hpp>
|
||||
#include "animstate.hpp"
|
||||
#include "render.hpp"
|
||||
|
||||
static float sprite_w_scale;
|
||||
static float sprite_h_scale;
|
||||
/*
|
||||
static void layershot_begin()
|
||||
{
|
||||
if(!config.cl_layershot)
|
||||
return;
|
||||
|
||||
gfx_clear(0,0,0);
|
||||
}
|
||||
|
||||
static void layershot_end()
|
||||
{
|
||||
if(!config.cl_layershot)
|
||||
return;
|
||||
|
||||
char buf[256];
|
||||
str_format(buf, sizeof(buf), "screenshots/layers_%04d.png", config.cl_layershot);
|
||||
gfx_screenshot_direct(buf);
|
||||
config.cl_layershot++;
|
||||
}*/
|
||||
|
||||
void select_sprite(SPRITE *spr, int flags, int sx, int sy)
|
||||
{
|
||||
int x = spr->x+sx;
|
||||
int y = spr->y+sy;
|
||||
int w = spr->w;
|
||||
int h = spr->h;
|
||||
int cx = spr->set->gridx;
|
||||
int cy = spr->set->gridy;
|
||||
|
||||
float f = sqrtf(h*h + w*w);
|
||||
sprite_w_scale = w/f;
|
||||
sprite_h_scale = h/f;
|
||||
|
||||
float x1 = x/(float)cx;
|
||||
float x2 = (x+w)/(float)cx;
|
||||
float y1 = y/(float)cy;
|
||||
float y2 = (y+h)/(float)cy;
|
||||
float temp = 0;
|
||||
|
||||
if(flags&SPRITE_FLAG_FLIP_Y)
|
||||
{
|
||||
temp = y1;
|
||||
y1 = y2;
|
||||
y2 = temp;
|
||||
}
|
||||
|
||||
if(flags&SPRITE_FLAG_FLIP_X)
|
||||
{
|
||||
temp = x1;
|
||||
x1 = x2;
|
||||
x2 = temp;
|
||||
}
|
||||
|
||||
gfx_quads_setsubset(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
void select_sprite(int id, int flags, int sx, int sy)
|
||||
{
|
||||
if(id < 0 || id > data->num_sprites)
|
||||
return;
|
||||
select_sprite(&data->sprites[id], flags, sx, sy);
|
||||
}
|
||||
|
||||
void draw_sprite(float x, float y, float size)
|
||||
{
|
||||
gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale);
|
||||
}
|
||||
|
||||
void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners)
|
||||
{
|
||||
int num = 8;
|
||||
for(int i = 0; i < num; i+=2)
|
||||
{
|
||||
float a1 = i/(float)num * pi/2;
|
||||
float a2 = (i+1)/(float)num * pi/2;
|
||||
float a3 = (i+2)/(float)num * pi/2;
|
||||
float ca1 = cosf(a1);
|
||||
float ca2 = cosf(a2);
|
||||
float ca3 = cosf(a3);
|
||||
float sa1 = sinf(a1);
|
||||
float sa2 = sinf(a2);
|
||||
float sa3 = sinf(a3);
|
||||
|
||||
if(corners&1) // TL
|
||||
gfx_quads_draw_freeform(
|
||||
x+r, y+r,
|
||||
x+(1-ca1)*r, y+(1-sa1)*r,
|
||||
x+(1-ca3)*r, y+(1-sa3)*r,
|
||||
x+(1-ca2)*r, y+(1-sa2)*r);
|
||||
|
||||
if(corners&2) // TR
|
||||
gfx_quads_draw_freeform(
|
||||
x+w-r, y+r,
|
||||
x+w-r+ca1*r, y+(1-sa1)*r,
|
||||
x+w-r+ca3*r, y+(1-sa3)*r,
|
||||
x+w-r+ca2*r, y+(1-sa2)*r);
|
||||
|
||||
if(corners&4) // BL
|
||||
gfx_quads_draw_freeform(
|
||||
x+r, y+h-r,
|
||||
x+(1-ca1)*r, y+h-r+sa1*r,
|
||||
x+(1-ca3)*r, y+h-r+sa3*r,
|
||||
x+(1-ca2)*r, y+h-r+sa2*r);
|
||||
|
||||
if(corners&8) // BR
|
||||
gfx_quads_draw_freeform(
|
||||
x+w-r, y+h-r,
|
||||
x+w-r+ca1*r, y+h-r+sa1*r,
|
||||
x+w-r+ca3*r, y+h-r+sa3*r,
|
||||
x+w-r+ca2*r, y+h-r+sa2*r);
|
||||
}
|
||||
|
||||
gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center
|
||||
gfx_quads_drawTL(x+r, y, w-r*2, r); // top
|
||||
gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom
|
||||
gfx_quads_drawTL(x, y+r, r, h-r*2); // left
|
||||
gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right
|
||||
|
||||
if(!(corners&1)) gfx_quads_drawTL(x, y, r, r); // TL
|
||||
if(!(corners&2)) gfx_quads_drawTL(x+w, y, -r, r); // TR
|
||||
if(!(corners&4)) gfx_quads_drawTL(x, y+h, r, -r); // BL
|
||||
if(!(corners&8)) gfx_quads_drawTL(x+w, y+h, -r, -r); // BR
|
||||
}
|
||||
|
||||
void draw_round_rect(float x, float y, float w, float h, float r)
|
||||
{
|
||||
draw_round_rect_ext(x,y,w,h,r,0xf);
|
||||
}
|
||||
|
||||
void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
|
||||
{
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(color.r, color.g, color.b, color.a);
|
||||
draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui_scale(), corners);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void render_tee(ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos)
|
||||
{
|
||||
vec2 direction = dir;
|
||||
vec2 position = pos;
|
||||
|
||||
//gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
|
||||
gfx_texture_set(info->texture);
|
||||
gfx_quads_begin();
|
||||
//gfx_quads_draw(pos.x, pos.y-128, 128, 128);
|
||||
|
||||
// first pass we draw the outline
|
||||
// second pass we draw the filling
|
||||
for(int p = 0; p < 2; p++)
|
||||
{
|
||||
int outline = p==0 ? 1 : 0;
|
||||
|
||||
for(int f = 0; f < 2; f++)
|
||||
{
|
||||
float animscale = info->size * 1.0f/64.0f;
|
||||
float basesize = info->size;
|
||||
if(f == 1)
|
||||
{
|
||||
gfx_quads_setrotation(anim->body.angle*pi*2);
|
||||
|
||||
// draw body
|
||||
gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, 1.0f);
|
||||
vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale;
|
||||
select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0);
|
||||
gfx_quads_draw(body_pos.x, body_pos.y, basesize, basesize);
|
||||
|
||||
// draw eyes
|
||||
if(p == 1)
|
||||
{
|
||||
switch (emote)
|
||||
{
|
||||
case EMOTE_PAIN:
|
||||
select_sprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0);
|
||||
break;
|
||||
case EMOTE_HAPPY:
|
||||
select_sprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0);
|
||||
break;
|
||||
case EMOTE_SURPRISE:
|
||||
select_sprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0);
|
||||
break;
|
||||
case EMOTE_ANGRY:
|
||||
select_sprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0);
|
||||
break;
|
||||
default:
|
||||
select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
float eyescale = basesize*0.40f;
|
||||
float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale;
|
||||
float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize;
|
||||
vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize;
|
||||
gfx_quads_draw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h);
|
||||
gfx_quads_draw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h);
|
||||
}
|
||||
}
|
||||
|
||||
// draw feet
|
||||
ANIM_KEYFRAME *foot = f ? &anim->front_foot : &anim->back_foot;
|
||||
|
||||
float w = basesize;
|
||||
float h = basesize/2;
|
||||
|
||||
gfx_quads_setrotation(foot->angle*pi*2);
|
||||
|
||||
bool indicate = !info->got_airjump && config.cl_airjumpindicator;
|
||||
float cs = 1.0f; // color scale
|
||||
|
||||
if(outline)
|
||||
select_sprite(SPRITE_TEE_FOOT_OUTLINE, 0, 0, 0);
|
||||
else
|
||||
{
|
||||
select_sprite(SPRITE_TEE_FOOT, 0, 0, 0);
|
||||
if(indicate)
|
||||
cs = 0.5f;
|
||||
}
|
||||
|
||||
gfx_setcolor(info->color_feet.r*cs, info->color_feet.g*cs, info->color_feet.b*cs, 1.0f);
|
||||
gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
gfx_quads_end();
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void calc_screen_params(float amount, float wmax, float hmax, float aspect, float *w, float *h)
|
||||
{
|
||||
float f = sqrt(amount) / sqrt(aspect);
|
||||
*w = f*aspect;
|
||||
*h = f;
|
||||
|
||||
// limit the view
|
||||
if(*w > wmax)
|
||||
{
|
||||
*w = wmax;
|
||||
*h = *w/aspect;
|
||||
}
|
||||
|
||||
if(*h > hmax)
|
||||
{
|
||||
*h = hmax;
|
||||
*w = *h*aspect;
|
||||
}
|
||||
}
|
||||
|
||||
void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
|
||||
float offset_x, float offset_y, float aspect, float zoom, float *points)
|
||||
{
|
||||
float width, height;
|
||||
calc_screen_params(1150*1000, 1500, 1050, aspect, &width, &height);
|
||||
center_x *= parallax_x;
|
||||
center_y *= parallax_y;
|
||||
width *= zoom;
|
||||
height *= zoom;
|
||||
points[0] = offset_x+center_x-width/2;
|
||||
points[1] = offset_y+center_y-height/2;
|
||||
points[2] = offset_x+center_x+width/2;
|
||||
points[3] = offset_y+center_y+height/2;
|
||||
}
|
||||
|
||||
void render_tilemap_generate_skip()
|
||||
{
|
||||
for(int g = 0; g < layers_num_groups(); g++)
|
||||
{
|
||||
MAPITEM_GROUP *group = layers_get_group(g);
|
||||
|
||||
for(int l = 0; l < group->num_layers; l++)
|
||||
{
|
||||
MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l);
|
||||
|
||||
if(layer->type == LAYERTYPE_TILES)
|
||||
{
|
||||
MAPITEM_LAYER_TILEMAP *tmap = (MAPITEM_LAYER_TILEMAP *)layer;
|
||||
TILE *tiles = (TILE *)map_get_data(tmap->data);
|
||||
for(int y = 0; y < tmap->height; y++)
|
||||
{
|
||||
for(int x = 1; x < tmap->width; x++)
|
||||
{
|
||||
int sx;
|
||||
for(sx = 1; x+sx < tmap->width && sx < 255; sx++)
|
||||
{
|
||||
if(tiles[y*tmap->width+x+sx].index)
|
||||
break;
|
||||
}
|
||||
|
||||
tiles[y*tmap->width+x].skip = sx-1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
project/jni/application/teeworlds/src/game/client/render.hpp
Normal file
68
project/jni/application/teeworlds/src/game/client/render.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef GAME_CLIENT_RENDER_H
|
||||
#define GAME_CLIENT_RENDER_H
|
||||
|
||||
#include <base/vmath.hpp>
|
||||
|
||||
#include "../mapitems.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
struct TEE_RENDER_INFO
|
||||
{
|
||||
TEE_RENDER_INFO()
|
||||
{
|
||||
texture = -1;
|
||||
color_body = vec4(1,1,1,1);
|
||||
color_feet = vec4(1,1,1,1);
|
||||
size = 1.0f;
|
||||
got_airjump = 1;
|
||||
};
|
||||
|
||||
int texture;
|
||||
vec4 color_body;
|
||||
vec4 color_feet;
|
||||
float size;
|
||||
int got_airjump;
|
||||
};
|
||||
|
||||
// sprite renderings
|
||||
enum
|
||||
{
|
||||
SPRITE_FLAG_FLIP_Y=1,
|
||||
SPRITE_FLAG_FLIP_X=2,
|
||||
|
||||
LAYERRENDERFLAG_OPAQUE=1,
|
||||
LAYERRENDERFLAG_TRANSPARENT=2,
|
||||
|
||||
TILERENDERFLAG_EXTEND=4,
|
||||
};
|
||||
|
||||
struct SPRITE;
|
||||
|
||||
void select_sprite(SPRITE *spr, int flags=0, int sx=0, int sy=0);
|
||||
void select_sprite(int id, int flags=0, int sx=0, int sy=0);
|
||||
|
||||
void draw_sprite(float x, float y, float size);
|
||||
|
||||
// rects
|
||||
void draw_round_rect(float x, float y, float w, float h, float r);
|
||||
void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners);
|
||||
void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding);
|
||||
|
||||
// larger rendering methods
|
||||
void render_tilemap_generate_skip();
|
||||
|
||||
// object render methods (gc_render_obj.cpp)
|
||||
void render_tee(class ANIMSTATE *anim, TEE_RENDER_INFO *info, int emote, vec2 dir, vec2 pos);
|
||||
|
||||
// map render methods (gc_render_map.cpp)
|
||||
void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result);
|
||||
void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels), int flags);
|
||||
void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int flags);
|
||||
|
||||
// helpers
|
||||
void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
|
||||
float offset_x, float offset_y, float aspect, float zoom, float *points);
|
||||
|
||||
|
||||
#endif
|
||||
274
project/jni/application/teeworlds/src/game/client/render_map.cpp
Normal file
274
project/jni/application/teeworlds/src/game/client/render_map.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <math.h>
|
||||
#include <base/math.hpp>
|
||||
#include <engine/e_client_interface.h>
|
||||
|
||||
#include "render.hpp"
|
||||
|
||||
void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result)
|
||||
{
|
||||
if(num_points == 0)
|
||||
{
|
||||
result[0] = 0;
|
||||
result[1] = 0;
|
||||
result[2] = 0;
|
||||
result[3] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(num_points == 1)
|
||||
{
|
||||
result[0] = fx2f(points[0].values[0]);
|
||||
result[1] = fx2f(points[0].values[1]);
|
||||
result[2] = fx2f(points[0].values[2]);
|
||||
result[3] = fx2f(points[0].values[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
time = fmod(time, points[num_points-1].time/1000.0f)*1000.0f;
|
||||
for(int i = 0; i < num_points-1; i++)
|
||||
{
|
||||
if(time >= points[i].time && time <= points[i+1].time)
|
||||
{
|
||||
float delta = points[i+1].time-points[i].time;
|
||||
float a = (time-points[i].time)/delta;
|
||||
|
||||
|
||||
if(points[i].curvetype == CURVETYPE_SMOOTH)
|
||||
a = -2*a*a*a + 3*a*a; // second hermite basis
|
||||
else if(points[i].curvetype == CURVETYPE_SLOW)
|
||||
a = a*a*a;
|
||||
else if(points[i].curvetype == CURVETYPE_FAST)
|
||||
{
|
||||
a = 1-a;
|
||||
a = 1-a*a*a;
|
||||
}
|
||||
else if (points[i].curvetype == CURVETYPE_STEP)
|
||||
a = 0;
|
||||
else
|
||||
{
|
||||
// linear
|
||||
}
|
||||
|
||||
for(int c = 0; c < channels; c++)
|
||||
{
|
||||
float v0 = fx2f(points[i].values[c]);
|
||||
float v1 = fx2f(points[i+1].values[c]);
|
||||
result[c] = v0 + (v1-v0) * a;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result[0] = fx2f(points[num_points-1].values[0]);
|
||||
result[1] = fx2f(points[num_points-1].values[1]);
|
||||
result[2] = fx2f(points[num_points-1].values[2]);
|
||||
result[3] = fx2f(points[num_points-1].values[3]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void rotate(POINT *center, POINT *point, float rotation)
|
||||
{
|
||||
int x = point->x - center->x;
|
||||
int y = point->y - center->y;
|
||||
point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x);
|
||||
point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y);
|
||||
}
|
||||
|
||||
void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels), int renderflags)
|
||||
{
|
||||
gfx_quads_begin();
|
||||
float conv = 1/255.0f;
|
||||
for(int i = 0; i < num_quads; i++)
|
||||
{
|
||||
QUAD *q = &quads[i];
|
||||
|
||||
float r=1, g=1, b=1, a=1;
|
||||
|
||||
if(q->color_env >= 0)
|
||||
{
|
||||
float channels[4];
|
||||
eval(q->color_env_offset/1000.0f, q->color_env, channels);
|
||||
r = channels[0];
|
||||
g = channels[1];
|
||||
b = channels[2];
|
||||
a = channels[3];
|
||||
}
|
||||
|
||||
bool opaque = false;
|
||||
if(a < 0.01f || (q->colors[0].a < 0.01f && q->colors[1].a < 0.01f && q->colors[2].a < 0.01f && q->colors[3].a < 0.01f))
|
||||
opaque = true;
|
||||
|
||||
if(opaque && !(renderflags&LAYERRENDERFLAG_OPAQUE))
|
||||
continue;
|
||||
if(!opaque && !(renderflags&LAYERRENDERFLAG_TRANSPARENT))
|
||||
continue;
|
||||
|
||||
gfx_quads_setsubset_free(
|
||||
fx2f(q->texcoords[0].x), fx2f(q->texcoords[0].y),
|
||||
fx2f(q->texcoords[1].x), fx2f(q->texcoords[1].y),
|
||||
fx2f(q->texcoords[2].x), fx2f(q->texcoords[2].y),
|
||||
fx2f(q->texcoords[3].x), fx2f(q->texcoords[3].y)
|
||||
);
|
||||
|
||||
float offset_x = 0;
|
||||
float offset_y = 0;
|
||||
float rot = 0;
|
||||
|
||||
// TODO: fix this
|
||||
if(q->pos_env >= 0)
|
||||
{
|
||||
float channels[4];
|
||||
eval(q->pos_env_offset/1000.0f, q->pos_env, channels);
|
||||
offset_x = channels[0];
|
||||
offset_y = channels[1];
|
||||
rot = channels[2]/360.0f*pi*2;
|
||||
}
|
||||
|
||||
|
||||
gfx_setcolorvertex(0, q->colors[0].r*conv*r, q->colors[0].g*conv*g, q->colors[0].b*conv*b, q->colors[0].a*conv*a);
|
||||
gfx_setcolorvertex(1, q->colors[1].r*conv*r, q->colors[1].g*conv*g, q->colors[1].b*conv*b, q->colors[1].a*conv*a);
|
||||
gfx_setcolorvertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a);
|
||||
gfx_setcolorvertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a);
|
||||
|
||||
POINT *points = q->points;
|
||||
|
||||
if(rot != 0)
|
||||
{
|
||||
static POINT rotated[4];
|
||||
rotated[0] = q->points[0];
|
||||
rotated[1] = q->points[1];
|
||||
rotated[2] = q->points[2];
|
||||
rotated[3] = q->points[3];
|
||||
points = rotated;
|
||||
|
||||
rotate(&q->points[4], &rotated[0], rot);
|
||||
rotate(&q->points[4], &rotated[1], rot);
|
||||
rotate(&q->points[4], &rotated[2], rot);
|
||||
rotate(&q->points[4], &rotated[3], rot);
|
||||
}
|
||||
|
||||
gfx_quads_draw_freeform(
|
||||
fx2f(points[0].x)+offset_x, fx2f(points[0].y)+offset_y,
|
||||
fx2f(points[1].x)+offset_x, fx2f(points[1].y)+offset_y,
|
||||
fx2f(points[2].x)+offset_x, fx2f(points[2].y)+offset_y,
|
||||
fx2f(points[3].x)+offset_x, fx2f(points[3].y)+offset_y
|
||||
);
|
||||
}
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
||||
void render_tilemap(TILE *tiles, int w, int h, float scale, vec4 color, int renderflags)
|
||||
{
|
||||
//gfx_texture_set(img_get(tmap->image));
|
||||
float screen_x0, screen_y0, screen_x1, screen_y1;
|
||||
gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
|
||||
//gfx_mapscreen(screen_x0-50, screen_y0-50, screen_x1+50, screen_y1+50);
|
||||
|
||||
// calculate the final pixelsize for the tiles
|
||||
float tile_pixelsize = 1024/32.0f;
|
||||
float final_tilesize = scale/(screen_x1-screen_x0) * gfx_screenwidth();
|
||||
float final_tilesize_scale = final_tilesize/tile_pixelsize;
|
||||
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(color.r, color.g, color.b, color.a);
|
||||
|
||||
int starty = (int)(screen_y0/scale)-1;
|
||||
int startx = (int)(screen_x0/scale)-1;
|
||||
int endy = (int)(screen_y1/scale)+1;
|
||||
int endx = (int)(screen_x1/scale)+1;
|
||||
|
||||
// adjust the texture shift according to mipmap level
|
||||
float texsize = 1024.0f;
|
||||
float frac = (1.25f/texsize) * (1/final_tilesize_scale);
|
||||
float nudge = (0.5f/texsize) * (1/final_tilesize_scale);
|
||||
|
||||
for(int y = starty; y < endy; y++)
|
||||
for(int x = startx; x < endx; x++)
|
||||
{
|
||||
int mx = x;
|
||||
int my = y;
|
||||
|
||||
if(renderflags&TILERENDERFLAG_EXTEND)
|
||||
{
|
||||
if(mx<0)
|
||||
mx = 0;
|
||||
if(mx>=w)
|
||||
mx = w-1;
|
||||
if(my<0)
|
||||
my = 0;
|
||||
if(my>=h)
|
||||
my = h-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mx<0)
|
||||
continue; // mx = 0;
|
||||
if(mx>=w)
|
||||
continue; // mx = w-1;
|
||||
if(my<0)
|
||||
continue; // my = 0;
|
||||
if(my>=h)
|
||||
continue; // my = h-1;
|
||||
}
|
||||
|
||||
int c = mx + my*w;
|
||||
|
||||
unsigned char index = tiles[c].index;
|
||||
if(index)
|
||||
{
|
||||
unsigned char flags = tiles[c].flags;
|
||||
|
||||
bool render = false;
|
||||
if(flags&TILEFLAG_OPAQUE)
|
||||
{
|
||||
if(renderflags&LAYERRENDERFLAG_OPAQUE)
|
||||
render = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(renderflags&LAYERRENDERFLAG_TRANSPARENT)
|
||||
render = true;
|
||||
}
|
||||
|
||||
if(render)
|
||||
{
|
||||
|
||||
int tx = index%16;
|
||||
int ty = index/16;
|
||||
int px0 = tx*(1024/16);
|
||||
int py0 = ty*(1024/16);
|
||||
int px1 = (tx+1)*(1024/16)-1;
|
||||
int py1 = (ty+1)*(1024/16)-1;
|
||||
|
||||
float u0 = nudge + px0/texsize+frac;
|
||||
float v0 = nudge + py0/texsize+frac;
|
||||
float u1 = nudge + px1/texsize-frac;
|
||||
float v1 = nudge + py1/texsize-frac;
|
||||
|
||||
if(flags&TILEFLAG_VFLIP)
|
||||
{
|
||||
float tmp = u0;
|
||||
u0 = u1;
|
||||
u1 = tmp;
|
||||
}
|
||||
|
||||
if(flags&TILEFLAG_HFLIP)
|
||||
{
|
||||
float tmp = v0;
|
||||
v0 = v1;
|
||||
v1 = tmp;
|
||||
}
|
||||
|
||||
gfx_quads_setsubset(u0,v0,u1,v1);
|
||||
gfx_quads_drawTL(x*scale, y*scale, scale, scale);
|
||||
}
|
||||
}
|
||||
x += tiles[c].skip;
|
||||
}
|
||||
|
||||
gfx_quads_end();
|
||||
gfx_mapscreen(screen_x0, screen_y0, screen_x1, screen_y1);
|
||||
}
|
||||
297
project/jni/application/teeworlds/src/game/client/ui.cpp
Normal file
297
project/jni/application/teeworlds/src/game/client/ui.cpp
Normal file
@@ -0,0 +1,297 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include "ui.hpp"
|
||||
|
||||
/********************************************************
|
||||
UI
|
||||
*********************************************************/
|
||||
static const void *hot_item = 0;
|
||||
static const void *active_item = 0;
|
||||
static const void *last_active_item = 0;
|
||||
static const void *becomming_hot_item = 0;
|
||||
static float mouse_x, mouse_y; /* in gui space */
|
||||
static float mouse_wx, mouse_wy; /* in world space */
|
||||
static unsigned mouse_buttons = 0;
|
||||
static unsigned last_mouse_buttons = 0;
|
||||
|
||||
float ui_mouse_x() { return mouse_x; }
|
||||
float ui_mouse_y() { return mouse_y; }
|
||||
float ui_mouse_world_x() { return mouse_wx; }
|
||||
float ui_mouse_world_y() { return mouse_wy; }
|
||||
int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; }
|
||||
int ui_mouse_button_clicked(int index) { return ui_mouse_button(index) && !((last_mouse_buttons>>index)&1) ; }
|
||||
|
||||
void ui_set_hot_item(const void *id) { becomming_hot_item = id; }
|
||||
void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; }
|
||||
void ui_clear_last_active_item() { last_active_item = 0; }
|
||||
const void *ui_hot_item() { return hot_item; }
|
||||
const void *ui_next_hot_item() { return becomming_hot_item; }
|
||||
const void *ui_active_item() { return active_item; }
|
||||
const void *ui_last_active_item() { return last_active_item; }
|
||||
|
||||
int ui_update(float mx, float my, float mwx, float mwy, int buttons)
|
||||
{
|
||||
mouse_x = mx;
|
||||
mouse_y = my;
|
||||
mouse_wx = mwx;
|
||||
mouse_wy = mwy;
|
||||
last_mouse_buttons = mouse_buttons;
|
||||
mouse_buttons = buttons;
|
||||
hot_item = becomming_hot_item;
|
||||
if(active_item)
|
||||
hot_item = active_item;
|
||||
becomming_hot_item = 0;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
bool ui_
|
||||
*/
|
||||
int ui_mouse_inside(const RECT *r)
|
||||
{
|
||||
if(mouse_x >= r->x && mouse_x <= r->x+r->w && mouse_y >= r->y && mouse_y <= r->y+r->h)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static RECT screen = { 0.0f, 0.0f, 848.0f, 480.0f };
|
||||
|
||||
RECT *ui_screen()
|
||||
{
|
||||
float aspect = gfx_screenaspect();
|
||||
float w, h;
|
||||
|
||||
h = 600;
|
||||
w = aspect*h;
|
||||
|
||||
screen.w = w;
|
||||
screen.h = h;
|
||||
|
||||
return &screen;
|
||||
}
|
||||
|
||||
void ui_set_scale(float s)
|
||||
{
|
||||
config.ui_scale = (int)(s*100.0f);
|
||||
}
|
||||
|
||||
float ui_scale()
|
||||
{
|
||||
return config.ui_scale/100.0f;
|
||||
}
|
||||
|
||||
void ui_clip_enable(const RECT *r)
|
||||
{
|
||||
float xscale = gfx_screenwidth()/ui_screen()->w;
|
||||
float yscale = gfx_screenheight()/ui_screen()->h;
|
||||
gfx_clip_enable((int)(r->x*xscale), (int)(r->y*yscale), (int)(r->w*xscale), (int)(r->h*yscale));
|
||||
}
|
||||
|
||||
void ui_clip_disable()
|
||||
{
|
||||
gfx_clip_disable();
|
||||
}
|
||||
|
||||
void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
if (top)
|
||||
{
|
||||
top->x = r.x;
|
||||
top->y = r.y;
|
||||
top->w = r.w;
|
||||
top->h = cut;
|
||||
}
|
||||
|
||||
if (bottom)
|
||||
{
|
||||
bottom->x = r.x;
|
||||
bottom->y = r.y + cut;
|
||||
bottom->w = r.w;
|
||||
bottom->h = r.h - cut;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
if (top)
|
||||
{
|
||||
top->x = r.x;
|
||||
top->y = r.y;
|
||||
top->w = r.w;
|
||||
top->h = r.h - cut;
|
||||
}
|
||||
|
||||
if (bottom)
|
||||
{
|
||||
bottom->x = r.x;
|
||||
bottom->y = r.y + r.h - cut;
|
||||
bottom->w = r.w;
|
||||
bottom->h = cut;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right)
|
||||
{
|
||||
RECT r = *original;
|
||||
float cut = r.w/2;
|
||||
|
||||
if (left)
|
||||
{
|
||||
left->x = r.x;
|
||||
left->y = r.y;
|
||||
left->w = cut;
|
||||
left->h = r.h;
|
||||
}
|
||||
|
||||
if (right)
|
||||
{
|
||||
right->x = r.x + cut;
|
||||
right->y = r.y;
|
||||
right->w = r.w - cut;
|
||||
right->h = r.h;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
if (left)
|
||||
{
|
||||
left->x = r.x;
|
||||
left->y = r.y;
|
||||
left->w = cut;
|
||||
left->h = r.h;
|
||||
}
|
||||
|
||||
if (right)
|
||||
{
|
||||
right->x = r.x + cut;
|
||||
right->y = r.y;
|
||||
right->w = r.w - cut;
|
||||
right->h = r.h;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
if (left)
|
||||
{
|
||||
left->x = r.x;
|
||||
left->y = r.y;
|
||||
left->w = r.w - cut;
|
||||
left->h = r.h;
|
||||
}
|
||||
|
||||
if (right)
|
||||
{
|
||||
right->x = r.x + r.w - cut;
|
||||
right->y = r.y;
|
||||
right->w = cut;
|
||||
right->h = r.h;
|
||||
}
|
||||
}
|
||||
|
||||
void ui_margin(const RECT *original, float cut, RECT *other_rect)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
other_rect->x = r.x + cut;
|
||||
other_rect->y = r.y + cut;
|
||||
other_rect->w = r.w - 2*cut;
|
||||
other_rect->h = r.h - 2*cut;
|
||||
}
|
||||
|
||||
void ui_vmargin(const RECT *original, float cut, RECT *other_rect)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
other_rect->x = r.x + cut;
|
||||
other_rect->y = r.y;
|
||||
other_rect->w = r.w - 2*cut;
|
||||
other_rect->h = r.h;
|
||||
}
|
||||
|
||||
void ui_hmargin(const RECT *original, float cut, RECT *other_rect)
|
||||
{
|
||||
RECT r = *original;
|
||||
cut *= ui_scale();
|
||||
|
||||
other_rect->x = r.x;
|
||||
other_rect->y = r.y + cut;
|
||||
other_rect->w = r.w;
|
||||
other_rect->h = r.h - 2*cut;
|
||||
}
|
||||
|
||||
|
||||
int ui_do_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, const void *extra)
|
||||
{
|
||||
/* logic */
|
||||
int ret = 0;
|
||||
int inside = ui_mouse_inside(r);
|
||||
static int button_used = 0;
|
||||
|
||||
if(ui_active_item() == id)
|
||||
{
|
||||
if(!ui_mouse_button(button_used))
|
||||
{
|
||||
if(inside && checked >= 0)
|
||||
ret = 1+button_used;
|
||||
ui_set_active_item(0);
|
||||
}
|
||||
}
|
||||
else if(ui_hot_item() == id)
|
||||
{
|
||||
if(ui_mouse_button(0))
|
||||
{
|
||||
ui_set_active_item(id);
|
||||
button_used = 0;
|
||||
}
|
||||
|
||||
if(ui_mouse_button(1))
|
||||
{
|
||||
ui_set_active_item(id);
|
||||
button_used = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(inside)
|
||||
ui_set_hot_item(id);
|
||||
|
||||
if(draw_func)
|
||||
draw_func(id, text, checked, r, extra);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ui_do_label(const RECT *r, const char *text, float size, int align, int max_width)
|
||||
{
|
||||
gfx_blend_normal();
|
||||
size *= ui_scale();
|
||||
if(align == 0)
|
||||
{
|
||||
float tw = gfx_text_width(0, size, text, max_width);
|
||||
gfx_text(0, r->x + r->w/2-tw/2, r->y, size, text, max_width);
|
||||
}
|
||||
else if(align < 0)
|
||||
gfx_text(0, r->x, r->y, size, text, max_width);
|
||||
else if(align > 0)
|
||||
{
|
||||
float tw = gfx_text_width(0, size, text, max_width);
|
||||
gfx_text(0, r->x + r->w-tw, r->y, size, text, max_width);
|
||||
}
|
||||
}
|
||||
65
project/jni/application/teeworlds/src/game/client/ui.hpp
Normal file
65
project/jni/application/teeworlds/src/game/client/ui.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef FILE_GAME_CLIENT_UI_H
|
||||
#define FILE_GAME_CLIENT_UI_H
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x, y, w, h;
|
||||
} RECT;
|
||||
|
||||
enum
|
||||
{
|
||||
CORNER_TL=1,
|
||||
CORNER_TR=2,
|
||||
CORNER_BL=4,
|
||||
CORNER_BR=8,
|
||||
|
||||
CORNER_T=CORNER_TL|CORNER_TR,
|
||||
CORNER_B=CORNER_BL|CORNER_BR,
|
||||
CORNER_R=CORNER_TR|CORNER_BR,
|
||||
CORNER_L=CORNER_TL|CORNER_BL,
|
||||
|
||||
CORNER_ALL=CORNER_T|CORNER_B
|
||||
};
|
||||
|
||||
int ui_update(float mx, float my, float mwx, float mwy, int buttons);
|
||||
|
||||
float ui_mouse_x();
|
||||
float ui_mouse_y();
|
||||
float ui_mouse_world_x();
|
||||
float ui_mouse_world_y();
|
||||
int ui_mouse_button(int index);
|
||||
int ui_mouse_button_clicked(int index);
|
||||
|
||||
void ui_set_hot_item(const void *id);
|
||||
void ui_set_active_item(const void *id);
|
||||
void ui_clear_last_active_item();
|
||||
const void *ui_hot_item();
|
||||
const void *ui_next_hot_item();
|
||||
const void *ui_active_item();
|
||||
const void *ui_last_active_item();
|
||||
|
||||
int ui_mouse_inside(const RECT *r);
|
||||
|
||||
RECT *ui_screen();
|
||||
void ui_set_scale(float s);
|
||||
float ui_scale();
|
||||
void ui_clip_enable(const RECT *r);
|
||||
void ui_clip_disable();
|
||||
|
||||
void ui_hsplit_t(const RECT *original, float cut, RECT *top, RECT *bottom);
|
||||
void ui_hsplit_b(const RECT *original, float cut, RECT *top, RECT *bottom);
|
||||
void ui_vsplit_mid(const RECT *original, RECT *left, RECT *right);
|
||||
void ui_vsplit_l(const RECT *original, float cut, RECT *left, RECT *right);
|
||||
void ui_vsplit_r(const RECT *original, float cut, RECT *left, RECT *right);
|
||||
|
||||
void ui_margin(const RECT *original, float cut, RECT *other_rect);
|
||||
void ui_vmargin(const RECT *original, float cut, RECT *other_rect);
|
||||
void ui_hmargin(const RECT *original, float cut, RECT *other_rect);
|
||||
|
||||
typedef void (*ui_draw_button_func)(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
int ui_do_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, const void *extra);
|
||||
void ui_do_label(const RECT *r, const char *text, float size, int align, int max_width = -1);
|
||||
|
||||
|
||||
#endif
|
||||
87
project/jni/application/teeworlds/src/game/collision.cpp
Normal file
87
project/jni/application/teeworlds/src/game/collision.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <base/system.h>
|
||||
#include <base/math.hpp>
|
||||
#include <base/vmath.hpp>
|
||||
|
||||
#include <math.h>
|
||||
#include <engine/e_common_interface.h>
|
||||
#include <game/mapitems.hpp>
|
||||
#include <game/layers.hpp>
|
||||
#include <game/collision.hpp>
|
||||
|
||||
static TILE *tiles;
|
||||
static int width = 0;
|
||||
static int height = 0;
|
||||
|
||||
int col_width() { return width; }
|
||||
int col_height() { return height; }
|
||||
|
||||
int col_init()
|
||||
{
|
||||
width = layers_game_layer()->width;
|
||||
height = layers_game_layer()->height;
|
||||
tiles = (TILE *)map_get_data(layers_game_layer()->data);
|
||||
|
||||
for(int i = 0; i < width*height; i++)
|
||||
{
|
||||
int index = tiles[i].index;
|
||||
|
||||
if(index > 128)
|
||||
continue;
|
||||
|
||||
if(index == TILE_DEATH)
|
||||
tiles[i].index = COLFLAG_DEATH;
|
||||
else if(index == TILE_SOLID)
|
||||
tiles[i].index = COLFLAG_SOLID;
|
||||
else if(index == TILE_NOHOOK)
|
||||
tiles[i].index = COLFLAG_SOLID|COLFLAG_NOHOOK;
|
||||
else
|
||||
tiles[i].index = 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int col_get(int x, int y)
|
||||
{
|
||||
int nx = clamp(x/32, 0, width-1);
|
||||
int ny = clamp(y/32, 0, height-1);
|
||||
|
||||
if(tiles[ny*width+nx].index > 128)
|
||||
return 0;
|
||||
return tiles[ny*width+nx].index;
|
||||
}
|
||||
|
||||
int col_is_solid(int x, int y)
|
||||
{
|
||||
return col_get(x,y)&COLFLAG_SOLID;
|
||||
}
|
||||
|
||||
|
||||
// TODO: rewrite this smarter!
|
||||
int col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out_collision, vec2 *out_before_collision)
|
||||
{
|
||||
float d = distance(pos0, pos1);
|
||||
vec2 last = pos0;
|
||||
|
||||
for(float f = 0; f < d; f++)
|
||||
{
|
||||
float a = f/d;
|
||||
vec2 pos = mix(pos0, pos1, a);
|
||||
if(col_is_solid(round(pos.x), round(pos.y)))
|
||||
{
|
||||
if(out_collision)
|
||||
*out_collision = pos;
|
||||
if(out_before_collision)
|
||||
*out_before_collision = last;
|
||||
return col_get(round(pos.x), round(pos.y));
|
||||
}
|
||||
last = pos;
|
||||
}
|
||||
if(out_collision)
|
||||
*out_collision = pos1;
|
||||
if(out_before_collision)
|
||||
*out_before_collision = pos1;
|
||||
return 0;
|
||||
}
|
||||
21
project/jni/application/teeworlds/src/game/collision.hpp
Normal file
21
project/jni/application/teeworlds/src/game/collision.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef GAME_MAPRES_COL_H
|
||||
#define GAME_MAPRES_COL_H
|
||||
|
||||
#include <base/vmath.hpp>
|
||||
|
||||
enum
|
||||
{
|
||||
COLFLAG_SOLID=1,
|
||||
COLFLAG_DEATH=2,
|
||||
COLFLAG_NOHOOK=4,
|
||||
};
|
||||
|
||||
int col_init();
|
||||
int col_is_solid(int x, int y);
|
||||
int col_get(int x, int y);
|
||||
int col_width();
|
||||
int col_height();
|
||||
int col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out_collision, vec2 *out_before_collision);
|
||||
|
||||
#endif
|
||||
238
project/jni/application/teeworlds/src/game/editor/array.hpp
Executable file
238
project/jni/application/teeworlds/src/game/editor/array.hpp
Executable file
@@ -0,0 +1,238 @@
|
||||
|
||||
template <class T>
|
||||
class array
|
||||
{
|
||||
//
|
||||
//
|
||||
void init()
|
||||
{
|
||||
list = 0;
|
||||
clear();
|
||||
}
|
||||
|
||||
public:
|
||||
array()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
//
|
||||
array(const array &other)
|
||||
{
|
||||
init();
|
||||
setsize(other.len());
|
||||
for(int i = 0; i < len(); i++)
|
||||
(*this)[i] = other[i];
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
virtual ~array()
|
||||
{
|
||||
delete [] list;
|
||||
list = 0;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
void deleteall()
|
||||
{
|
||||
for(int i = 0; i < len(); i++)
|
||||
delete list[i];
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
void clear()
|
||||
{
|
||||
delete [] list;
|
||||
|
||||
list_size = 1;
|
||||
list = new T[1];
|
||||
num_elements = 0;
|
||||
}
|
||||
|
||||
int find(T val)
|
||||
{
|
||||
for(int i = 0; i < len(); i++)
|
||||
if((*this)[i] == val)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool exist(T val)
|
||||
{
|
||||
return find(val) != -1;
|
||||
}
|
||||
|
||||
//
|
||||
// returns the number of elements in the list
|
||||
//
|
||||
int len() const
|
||||
{
|
||||
return num_elements;
|
||||
}
|
||||
|
||||
//
|
||||
// This doesn't conserve the order in the list. Be careful
|
||||
//
|
||||
void removebyindexfast(int index)
|
||||
{
|
||||
//ASSUME(_Pos >= 0 && _Pos < num_elements);
|
||||
list[index] = list[num_elements-1];
|
||||
setsize(len()-1);
|
||||
}
|
||||
|
||||
void removefast(const T& _Elem)
|
||||
{
|
||||
for(int i = 0; i < len(); i++)
|
||||
if(list[i] == _Elem)
|
||||
{
|
||||
removebyindexfast(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
void removebyindex(int index)
|
||||
{
|
||||
//ASSUME(_Pos >= 0 && _Pos < num_elements);
|
||||
|
||||
for(int i = index+1; i < num_elements; i++)
|
||||
list[i-1] = list[i];
|
||||
|
||||
setsize(len()-1);
|
||||
}
|
||||
|
||||
void insert(int index, const T& element)
|
||||
{
|
||||
int some_len = len();
|
||||
if (index < some_len)
|
||||
setsize(some_len+1);
|
||||
else
|
||||
setsize(index + 1);
|
||||
|
||||
for(int i = num_elements-2; i >= index; i--)
|
||||
list[i+1] = list[i];
|
||||
|
||||
list[index] = element;
|
||||
}
|
||||
|
||||
bool remove(const T& element)
|
||||
{
|
||||
for(int i = 0; i < len(); i++)
|
||||
if(list[i] == element)
|
||||
{
|
||||
removebyindex(i);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
int add(const T& element)
|
||||
{
|
||||
//if(num_elements == list_size)
|
||||
setsize(len()+1);
|
||||
list[num_elements-1] = element;
|
||||
return num_elements-1;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
int add(const T& elem, int index)
|
||||
{
|
||||
setsize(len()+1);
|
||||
|
||||
for(int i = num_elements-1; i > index; i--)
|
||||
list[i] = list[i-1];
|
||||
|
||||
list[index] = elem;
|
||||
|
||||
//num_elements++;
|
||||
return num_elements-1;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
T& operator[] (int index)
|
||||
{
|
||||
return list[index];
|
||||
}
|
||||
|
||||
const T& operator[] (int index) const
|
||||
{
|
||||
return list[index];
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
T *getptr()
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
const T *getptr() const
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
void setsize(int new_len)
|
||||
{
|
||||
if (list_size < new_len)
|
||||
allocsize(new_len);
|
||||
num_elements = new_len;
|
||||
}
|
||||
|
||||
// removes unnessasary data, returns how many bytes was earned
|
||||
int optimize()
|
||||
{
|
||||
int Before = memoryusage();
|
||||
setsize(num_elements);
|
||||
return Before - memoryusage();
|
||||
}
|
||||
|
||||
// returns how much memory this dynamic array is using
|
||||
int memoryusage()
|
||||
{
|
||||
return sizeof(array) + sizeof(T)*list_size;
|
||||
}
|
||||
|
||||
//
|
||||
array &operator = (const array &other)
|
||||
{
|
||||
setsize(other.len());
|
||||
for(int i = 0; i < len(); i++)
|
||||
(*this)[i] = other[i];
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
void allocsize(int new_len)
|
||||
{
|
||||
list_size = new_len;
|
||||
T *new_list = new T[list_size];
|
||||
|
||||
long end = num_elements < list_size ? num_elements : list_size;
|
||||
for(int i = 0; i < end; i++)
|
||||
new_list[i] = list[i];
|
||||
|
||||
delete [] list;
|
||||
list = 0;
|
||||
num_elements = num_elements < list_size ? num_elements : list_size;
|
||||
list = new_list;
|
||||
}
|
||||
|
||||
T *list;
|
||||
long list_size;
|
||||
long num_elements;
|
||||
};
|
||||
|
||||
2772
project/jni/application/teeworlds/src/game/editor/ed_editor.cpp
Normal file
2772
project/jni/application/teeworlds/src/game/editor/ed_editor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
522
project/jni/application/teeworlds/src/game/editor/ed_editor.hpp
Normal file
522
project/jni/application/teeworlds/src/game/editor/ed_editor.hpp
Normal file
@@ -0,0 +1,522 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#include <base/system.h>
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "array.hpp"
|
||||
#include "../mapitems.hpp"
|
||||
#include "../client/render.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include <engine/e_client_interface.h>
|
||||
#include <engine/e_datafile.h>
|
||||
#include <engine/e_config.h>
|
||||
}
|
||||
|
||||
#include <game/client/ui.hpp>
|
||||
|
||||
typedef void (*INDEX_MODIFY_FUNC)(int *index);
|
||||
|
||||
// EDITOR SPECIFIC
|
||||
template<typename T>
|
||||
void swap(T &a, T &b)
|
||||
{
|
||||
T tmp = a;
|
||||
a = b;
|
||||
b = tmp;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
MODE_LAYERS=0,
|
||||
MODE_IMAGES,
|
||||
|
||||
DIALOG_NONE=0,
|
||||
DIALOG_FILE,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
POINT position;
|
||||
int type;
|
||||
} ENTITY;
|
||||
|
||||
class ENVELOPE
|
||||
{
|
||||
public:
|
||||
int channels;
|
||||
array<ENVPOINT> points;
|
||||
char name[32];
|
||||
float bottom, top;
|
||||
|
||||
ENVELOPE(int chan)
|
||||
{
|
||||
channels = chan;
|
||||
name[0] = 0;
|
||||
bottom = 0;
|
||||
top = 0;
|
||||
}
|
||||
|
||||
static int sort_comp(const void *v0, const void *v1)
|
||||
{
|
||||
const ENVPOINT *p0 = (const ENVPOINT *)v0;
|
||||
const ENVPOINT *p1 = (const ENVPOINT *)v1;
|
||||
if(p0->time < p1->time)
|
||||
return -1;
|
||||
if(p0->time > p1->time)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void resort()
|
||||
{
|
||||
qsort(points.getptr(), points.len(), sizeof(ENVPOINT), sort_comp);
|
||||
find_top_bottom(0xf);
|
||||
}
|
||||
|
||||
void find_top_bottom(int channelmask)
|
||||
{
|
||||
top = -1000000000.0f;
|
||||
bottom = 1000000000.0f;
|
||||
for(int i = 0; i < points.len(); i++)
|
||||
{
|
||||
for(int c = 0; c < channels; c++)
|
||||
{
|
||||
if(channelmask&(1<<c))
|
||||
{
|
||||
float v = fx2f(points[i].values[c]);
|
||||
if(v > top) top = v;
|
||||
if(v < bottom) bottom = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int eval(float time, float *result)
|
||||
{
|
||||
render_eval_envelope(points.getptr(), points.len(), channels, time, result);
|
||||
return channels;
|
||||
}
|
||||
|
||||
void add_point(int time, int v0, int v1=0, int v2=0, int v3=0)
|
||||
{
|
||||
ENVPOINT p;
|
||||
p.time = time;
|
||||
p.values[0] = v0;
|
||||
p.values[1] = v1;
|
||||
p.values[2] = v2;
|
||||
p.values[3] = v3;
|
||||
p.curvetype = CURVETYPE_LINEAR;
|
||||
points.add(p);
|
||||
resort();
|
||||
}
|
||||
|
||||
float end_time()
|
||||
{
|
||||
if(points.len())
|
||||
return points[points.len()-1].time*(1.0f/1000.0f);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class LAYER;
|
||||
class LAYERGROUP;
|
||||
class MAP;
|
||||
|
||||
class LAYER
|
||||
{
|
||||
public:
|
||||
LAYER()
|
||||
{
|
||||
type = LAYERTYPE_INVALID;
|
||||
type_name = "(invalid)";
|
||||
visible = true;
|
||||
readonly = false;
|
||||
flags = 0;
|
||||
}
|
||||
|
||||
virtual ~LAYER()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual void brush_selecting(RECT rect) {}
|
||||
virtual int brush_grab(LAYERGROUP *brush, RECT rect) { return 0; }
|
||||
virtual void brush_draw(LAYER *brush, float x, float y) {}
|
||||
virtual void brush_place(LAYER *brush, float x, float y) {}
|
||||
virtual void brush_flip_x() {}
|
||||
virtual void brush_flip_y() {}
|
||||
virtual void brush_rotate(float amount) {}
|
||||
|
||||
virtual void render() {}
|
||||
virtual int render_properties(RECT *toolbox) { return 0; }
|
||||
|
||||
virtual void modify_image_index(INDEX_MODIFY_FUNC func) {}
|
||||
virtual void modify_envelope_index(INDEX_MODIFY_FUNC func) {}
|
||||
|
||||
virtual void get_size(float *w, float *h) { *w = 0; *h = 0;}
|
||||
|
||||
const char *type_name;
|
||||
int type;
|
||||
int flags;
|
||||
|
||||
bool readonly;
|
||||
bool visible;
|
||||
};
|
||||
|
||||
class LAYERGROUP
|
||||
{
|
||||
public:
|
||||
array<LAYER*> layers;
|
||||
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
|
||||
int parallax_x;
|
||||
int parallax_y;
|
||||
|
||||
int use_clipping;
|
||||
int clip_x;
|
||||
int clip_y;
|
||||
int clip_w;
|
||||
int clip_h;
|
||||
|
||||
const char *name;
|
||||
bool game_group;
|
||||
bool visible;
|
||||
|
||||
LAYERGROUP();
|
||||
~LAYERGROUP();
|
||||
|
||||
void convert(RECT *rect);
|
||||
void render();
|
||||
void mapscreen();
|
||||
void mapping(float *points);
|
||||
|
||||
bool is_empty() const;
|
||||
void clear();
|
||||
void add_layer(LAYER *l);
|
||||
|
||||
void get_size(float *w, float *h);
|
||||
|
||||
void delete_layer(int index);
|
||||
int swap_layers(int index0, int index1);
|
||||
|
||||
void modify_image_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
for(int i = 0; i < layers.len(); i++)
|
||||
layers[i]->modify_image_index(func);
|
||||
}
|
||||
|
||||
void modify_envelope_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
for(int i = 0; i < layers.len(); i++)
|
||||
layers[i]->modify_envelope_index(func);
|
||||
}
|
||||
};
|
||||
|
||||
class EDITOR_IMAGE : public IMAGE_INFO
|
||||
{
|
||||
public:
|
||||
EDITOR_IMAGE()
|
||||
{
|
||||
tex_id = -1;
|
||||
name[0] = 0;
|
||||
external = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
data = 0;
|
||||
format = 0;
|
||||
}
|
||||
|
||||
~EDITOR_IMAGE()
|
||||
{
|
||||
gfx_unload_texture(tex_id);
|
||||
}
|
||||
|
||||
void analyse_tileflags();
|
||||
|
||||
int tex_id;
|
||||
int external;
|
||||
char name[128];
|
||||
unsigned char tileflags[256];
|
||||
};
|
||||
|
||||
class MAP
|
||||
{
|
||||
void make_game_group(LAYERGROUP *group);
|
||||
void make_game_layer(LAYER *layer);
|
||||
public:
|
||||
MAP()
|
||||
{
|
||||
clean();
|
||||
}
|
||||
|
||||
array<LAYERGROUP*> groups;
|
||||
array<EDITOR_IMAGE*> images;
|
||||
array<ENVELOPE*> envelopes;
|
||||
|
||||
class LAYER_GAME *game_layer;
|
||||
LAYERGROUP *game_group;
|
||||
|
||||
ENVELOPE *new_envelope(int channels)
|
||||
{
|
||||
ENVELOPE *e = new ENVELOPE(channels);
|
||||
envelopes.add(e);
|
||||
return e;
|
||||
}
|
||||
|
||||
LAYERGROUP *new_group()
|
||||
{
|
||||
LAYERGROUP *g = new LAYERGROUP;
|
||||
groups.add(g);
|
||||
return g;
|
||||
}
|
||||
|
||||
int swap_groups(int index0, int index1)
|
||||
{
|
||||
if(index0 < 0 || index0 >= groups.len()) return index0;
|
||||
if(index1 < 0 || index1 >= groups.len()) return index0;
|
||||
if(index0 == index1) return index0;
|
||||
swap(groups[index0], groups[index1]);
|
||||
return index1;
|
||||
}
|
||||
|
||||
void delete_group(int index)
|
||||
{
|
||||
if(index < 0 || index >= groups.len()) return;
|
||||
delete groups[index];
|
||||
groups.removebyindex(index);
|
||||
}
|
||||
|
||||
void modify_image_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
for(int i = 0; i < groups.len(); i++)
|
||||
groups[i]->modify_image_index(func);
|
||||
}
|
||||
|
||||
void modify_envelope_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
for(int i = 0; i < groups.len(); i++)
|
||||
groups[i]->modify_envelope_index(func);
|
||||
}
|
||||
|
||||
void clean();
|
||||
void create_default(int entities_texture);
|
||||
|
||||
// io
|
||||
int save(const char *filename);
|
||||
int load(const char *filename);
|
||||
};
|
||||
|
||||
|
||||
struct PROPERTY
|
||||
{
|
||||
const char *name;
|
||||
int value;
|
||||
int type;
|
||||
int min;
|
||||
int max;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROPTYPE_NULL=0,
|
||||
PROPTYPE_BOOL,
|
||||
PROPTYPE_INT_STEP,
|
||||
PROPTYPE_INT_SCROLL,
|
||||
PROPTYPE_COLOR,
|
||||
PROPTYPE_IMAGE,
|
||||
PROPTYPE_ENVELOPE,
|
||||
};
|
||||
|
||||
class EDITOR
|
||||
{
|
||||
public:
|
||||
EDITOR()
|
||||
{
|
||||
mode = MODE_LAYERS;
|
||||
dialog = 0;
|
||||
tooltip = 0;
|
||||
|
||||
world_offset_x = 0;
|
||||
world_offset_y = 0;
|
||||
editor_offset_x = 0.0f;
|
||||
editor_offset_y = 0.0f;
|
||||
|
||||
world_zoom = 1.0f;
|
||||
zoom_level = 100;
|
||||
lock_mouse = false;
|
||||
show_mouse_pointer = true;
|
||||
mouse_delta_x = 0;
|
||||
mouse_delta_y = 0;
|
||||
mouse_delta_wx = 0;
|
||||
mouse_delta_wy = 0;
|
||||
|
||||
gui_active = true;
|
||||
proof_borders = false;
|
||||
|
||||
show_detail = true;
|
||||
animate = false;
|
||||
animate_start = 0;
|
||||
animate_time = 0;
|
||||
animate_speed = 1;
|
||||
|
||||
show_envelope_editor = 0;
|
||||
}
|
||||
|
||||
void invoke_file_dialog(int listdir_type, const char *title, const char *button_text,
|
||||
const char *basepath, const char *default_name,
|
||||
void (*func)(const char *filename));
|
||||
|
||||
void reset(bool create_default=true);
|
||||
int save(const char *filename);
|
||||
int load(const char *filename);
|
||||
int append(const char *filename);
|
||||
void render();
|
||||
|
||||
QUAD *get_selected_quad();
|
||||
LAYER *get_selected_layer_type(int index, int type);
|
||||
LAYER *get_selected_layer(int index);
|
||||
LAYERGROUP *get_selected_group();
|
||||
|
||||
int do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val);
|
||||
|
||||
int mode;
|
||||
int dialog;
|
||||
const char *tooltip;
|
||||
|
||||
float world_offset_x;
|
||||
float world_offset_y;
|
||||
float editor_offset_x;
|
||||
float editor_offset_y;
|
||||
float world_zoom;
|
||||
int zoom_level;
|
||||
bool lock_mouse;
|
||||
bool show_mouse_pointer;
|
||||
bool gui_active;
|
||||
bool proof_borders;
|
||||
float mouse_delta_x;
|
||||
float mouse_delta_y;
|
||||
float mouse_delta_wx;
|
||||
float mouse_delta_wy;
|
||||
|
||||
bool show_detail;
|
||||
bool animate;
|
||||
int64 animate_start;
|
||||
float animate_time;
|
||||
float animate_speed;
|
||||
|
||||
int show_envelope_editor;
|
||||
|
||||
int selected_layer;
|
||||
int selected_group;
|
||||
int selected_quad;
|
||||
int selected_points;
|
||||
int selected_envelope;
|
||||
int selected_image;
|
||||
|
||||
MAP map;
|
||||
};
|
||||
|
||||
extern EDITOR editor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x, y;
|
||||
int w, h;
|
||||
} RECTi;
|
||||
|
||||
class LAYER_TILES : public LAYER
|
||||
{
|
||||
public:
|
||||
LAYER_TILES(int w, int h);
|
||||
~LAYER_TILES();
|
||||
|
||||
void resize(int new_w, int new_h);
|
||||
|
||||
void make_palette();
|
||||
virtual void render();
|
||||
|
||||
int convert_x(float x) const;
|
||||
int convert_y(float y) const;
|
||||
void convert(RECT rect, RECTi *out);
|
||||
void snap(RECT *rect);
|
||||
void clamp(RECTi *rect);
|
||||
|
||||
virtual void brush_selecting(RECT rect);
|
||||
virtual int brush_grab(LAYERGROUP *brush, RECT rect);
|
||||
virtual void brush_draw(LAYER *brush, float wx, float wy);
|
||||
virtual void brush_flip_x();
|
||||
virtual void brush_flip_y();
|
||||
|
||||
virtual int render_properties(RECT *toolbox);
|
||||
|
||||
virtual void modify_image_index(INDEX_MODIFY_FUNC func);
|
||||
virtual void modify_envelope_index(INDEX_MODIFY_FUNC func);
|
||||
|
||||
void prepare_for_save();
|
||||
|
||||
void get_size(float *w, float *h) { *w = width*32.0f; *h = height*32.0f; }
|
||||
|
||||
int tex_id;
|
||||
int game;
|
||||
int image;
|
||||
int width;
|
||||
int height;
|
||||
TILE *tiles;
|
||||
};
|
||||
|
||||
class LAYER_QUADS : public LAYER
|
||||
{
|
||||
public:
|
||||
LAYER_QUADS();
|
||||
~LAYER_QUADS();
|
||||
|
||||
virtual void render();
|
||||
QUAD *new_quad();
|
||||
|
||||
virtual void brush_selecting(RECT rect);
|
||||
virtual int brush_grab(LAYERGROUP *brush, RECT rect);
|
||||
virtual void brush_place(LAYER *brush, float wx, float wy);
|
||||
virtual void brush_flip_x();
|
||||
virtual void brush_flip_y();
|
||||
virtual void brush_rotate(float amount);
|
||||
|
||||
virtual int render_properties(RECT *toolbox);
|
||||
|
||||
virtual void modify_image_index(INDEX_MODIFY_FUNC func);
|
||||
virtual void modify_envelope_index(INDEX_MODIFY_FUNC func);
|
||||
|
||||
void get_size(float *w, float *h);
|
||||
|
||||
int image;
|
||||
array<QUAD> quads;
|
||||
};
|
||||
|
||||
class LAYER_GAME : public LAYER_TILES
|
||||
{
|
||||
public:
|
||||
LAYER_GAME(int w, int h);
|
||||
~LAYER_GAME();
|
||||
|
||||
virtual int render_properties(RECT *toolbox);
|
||||
};
|
||||
|
||||
int do_editor_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, int flags, const char *tooltip);
|
||||
void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
void draw_editor_button_menuitem(const void *id, const char *text, int checked, const RECT *r, const void *extra);
|
||||
|
||||
void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra=0);
|
||||
void ui_do_popup_menu();
|
||||
|
||||
int popup_group(RECT view);
|
||||
int popup_layer(RECT view);
|
||||
int popup_quad(RECT view);
|
||||
int popup_point(RECT view);
|
||||
|
||||
void popup_select_image_invoke(int current, float x, float y);
|
||||
int popup_select_image_result();
|
||||
602
project/jni/application/teeworlds/src/game/editor/ed_io.cpp
Normal file
602
project/jni/application/teeworlds/src/game/editor/ed_io.cpp
Normal file
@@ -0,0 +1,602 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "ed_editor.hpp"
|
||||
|
||||
template<typename T>
|
||||
static int make_version(int i, const T &v)
|
||||
{ return (i<<16)+sizeof(T); }
|
||||
|
||||
// backwards compatiblity
|
||||
void editor_load_old(DATAFILE *df, MAP *map)
|
||||
{
|
||||
class mapres_image
|
||||
{
|
||||
public:
|
||||
int width;
|
||||
int height;
|
||||
int image_data;
|
||||
};
|
||||
|
||||
struct mapres_tilemap
|
||||
{
|
||||
int image;
|
||||
int width;
|
||||
int height;
|
||||
int x, y;
|
||||
int scale;
|
||||
int data;
|
||||
int main;
|
||||
};
|
||||
|
||||
struct mapres_entity
|
||||
{
|
||||
int x, y;
|
||||
int data[1];
|
||||
};
|
||||
|
||||
struct mapres_spawnpoint
|
||||
{
|
||||
int x, y;
|
||||
};
|
||||
|
||||
struct mapres_item
|
||||
{
|
||||
int x, y;
|
||||
int type;
|
||||
};
|
||||
|
||||
struct mapres_flagstand
|
||||
{
|
||||
int x, y;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MAPRES_ENTS_START=1,
|
||||
MAPRES_SPAWNPOINT=1,
|
||||
MAPRES_ITEM=2,
|
||||
MAPRES_SPAWNPOINT_RED=3,
|
||||
MAPRES_SPAWNPOINT_BLUE=4,
|
||||
MAPRES_FLAGSTAND_RED=5,
|
||||
MAPRES_FLAGSTAND_BLUE=6,
|
||||
MAPRES_ENTS_END,
|
||||
|
||||
ITEM_NULL=0,
|
||||
ITEM_WEAPON_GUN=0x00010001,
|
||||
ITEM_WEAPON_SHOTGUN=0x00010002,
|
||||
ITEM_WEAPON_ROCKET=0x00010003,
|
||||
ITEM_WEAPON_SNIPER=0x00010004,
|
||||
ITEM_WEAPON_HAMMER=0x00010005,
|
||||
ITEM_HEALTH =0x00020001,
|
||||
ITEM_ARMOR=0x00030001,
|
||||
ITEM_NINJA=0x00040001,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MAPRES_REGISTERED=0x8000,
|
||||
MAPRES_IMAGE=0x8001,
|
||||
MAPRES_TILEMAP=0x8002,
|
||||
MAPRES_COLLISIONMAP=0x8003,
|
||||
MAPRES_TEMP_THEME=0x8fff,
|
||||
};
|
||||
|
||||
// load tilemaps
|
||||
int game_width = 0;
|
||||
int game_height = 0;
|
||||
{
|
||||
int start, num;
|
||||
datafile_get_type(df, MAPRES_TILEMAP, &start, &num);
|
||||
for(int t = 0; t < num; t++)
|
||||
{
|
||||
mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0);
|
||||
|
||||
LAYER_TILES *l = new LAYER_TILES(tmap->width, tmap->height);
|
||||
|
||||
if(tmap->main)
|
||||
{
|
||||
// move game layer to correct position
|
||||
for(int i = 0; i < map->groups[0]->layers.len()-1; i++)
|
||||
{
|
||||
if(map->groups[0]->layers[i] == editor.map.game_layer)
|
||||
map->groups[0]->swap_layers(i, i+1);
|
||||
}
|
||||
|
||||
game_width = tmap->width;
|
||||
game_height = tmap->height;
|
||||
}
|
||||
|
||||
// add new layer
|
||||
map->groups[0]->add_layer(l);
|
||||
|
||||
// process the data
|
||||
unsigned char *src_data = (unsigned char *)datafile_get_data(df, tmap->data);
|
||||
TILE *dst_data = l->tiles;
|
||||
|
||||
for(int y = 0; y < tmap->height; y++)
|
||||
for(int x = 0; x < tmap->width; x++, dst_data++, src_data+=2)
|
||||
{
|
||||
dst_data->index = src_data[0];
|
||||
dst_data->flags = src_data[1];
|
||||
}
|
||||
|
||||
l->image = tmap->image;
|
||||
}
|
||||
}
|
||||
|
||||
// load images
|
||||
{
|
||||
int start, count;
|
||||
datafile_get_type(df, MAPRES_IMAGE, &start, &count);
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
mapres_image *imgres = (mapres_image *)datafile_get_item(df, start+i, 0, 0);
|
||||
void *data = datafile_get_data(df, imgres->image_data);
|
||||
|
||||
EDITOR_IMAGE *img = new EDITOR_IMAGE;
|
||||
img->width = imgres->width;
|
||||
img->height = imgres->height;
|
||||
img->format = IMG_RGBA;
|
||||
|
||||
// copy image data
|
||||
img->data = mem_alloc(img->width*img->height*4, 1);
|
||||
mem_copy(img->data, data, img->width*img->height*4);
|
||||
img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO, 0);
|
||||
map->images.add(img);
|
||||
|
||||
// unload image
|
||||
datafile_unload_data(df, imgres->image_data);
|
||||
}
|
||||
}
|
||||
|
||||
// load entities
|
||||
{
|
||||
LAYER_GAME *g = map->game_layer;
|
||||
g->resize(game_width, game_height);
|
||||
for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++)
|
||||
{
|
||||
// fetch entities of this class
|
||||
int start, num;
|
||||
datafile_get_type(df, t, &start, &num);
|
||||
|
||||
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
mapres_entity *e = (mapres_entity *)datafile_get_item(df, start+i,0,0);
|
||||
int x = e->x/32;
|
||||
int y = e->y/32;
|
||||
int id = -1;
|
||||
|
||||
if(t == MAPRES_SPAWNPOINT) id = ENTITY_SPAWN;
|
||||
else if(t == MAPRES_SPAWNPOINT_RED) id = ENTITY_SPAWN_RED;
|
||||
else if(t == MAPRES_SPAWNPOINT_BLUE) id = ENTITY_SPAWN_BLUE;
|
||||
else if(t == MAPRES_FLAGSTAND_RED) id = ENTITY_FLAGSTAND_RED;
|
||||
else if(t == MAPRES_FLAGSTAND_BLUE) id = ENTITY_FLAGSTAND_BLUE;
|
||||
else if(t == MAPRES_ITEM)
|
||||
{
|
||||
if(e->data[0] == ITEM_WEAPON_SHOTGUN) id = ENTITY_WEAPON_SHOTGUN;
|
||||
else if(e->data[0] == ITEM_WEAPON_ROCKET) id = ENTITY_WEAPON_GRENADE;
|
||||
else if(e->data[0] == ITEM_NINJA) id = ENTITY_POWERUP_NINJA;
|
||||
else if(e->data[0] == ITEM_ARMOR) id = ENTITY_ARMOR_1;
|
||||
else if(e->data[0] == ITEM_HEALTH) id = ENTITY_HEALTH_1;
|
||||
}
|
||||
|
||||
if(id > 0 && x >= 0 && x < g->width && y >= 0 && y < g->height)
|
||||
g->tiles[y*g->width+x].index = id+ENTITY_OFFSET;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int EDITOR::save(const char *filename)
|
||||
{
|
||||
return map.save(filename);
|
||||
}
|
||||
|
||||
int MAP::save(const char *filename)
|
||||
{
|
||||
dbg_msg("editor", "saving to '%s'...", filename);
|
||||
DATAFILE_OUT *df = datafile_create(filename);
|
||||
if(!df)
|
||||
{
|
||||
dbg_msg("editor", "failed to open file '%s'...", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// save version
|
||||
{
|
||||
MAPITEM_VERSION item;
|
||||
item.version = 1;
|
||||
datafile_add_item(df, MAPITEMTYPE_VERSION, 0, sizeof(item), &item);
|
||||
}
|
||||
|
||||
// save images
|
||||
for(int i = 0; i < images.len(); i++)
|
||||
{
|
||||
EDITOR_IMAGE *img = images[i];
|
||||
|
||||
// analyse the image for when saving (should be done when we load the image)
|
||||
// TODO!
|
||||
img->analyse_tileflags();
|
||||
|
||||
MAPITEM_IMAGE item;
|
||||
item.version = 1;
|
||||
|
||||
item.width = img->width;
|
||||
item.height = img->height;
|
||||
item.external = img->external;
|
||||
item.image_name = datafile_add_data(df, strlen(img->name)+1, img->name);
|
||||
if(img->external)
|
||||
item.image_data = -1;
|
||||
else
|
||||
item.image_data = datafile_add_data(df, item.width*item.height*4, img->data);
|
||||
datafile_add_item(df, MAPITEMTYPE_IMAGE, i, sizeof(item), &item);
|
||||
}
|
||||
|
||||
// save layers
|
||||
int layer_count = 0;
|
||||
for(int g = 0; g < groups.len(); g++)
|
||||
{
|
||||
LAYERGROUP *group = groups[g];
|
||||
MAPITEM_GROUP gitem;
|
||||
gitem.version = MAPITEM_GROUP::CURRENT_VERSION;
|
||||
|
||||
gitem.parallax_x = group->parallax_x;
|
||||
gitem.parallax_y = group->parallax_y;
|
||||
gitem.offset_x = group->offset_x;
|
||||
gitem.offset_y = group->offset_y;
|
||||
gitem.use_clipping = group->use_clipping;
|
||||
gitem.clip_x = group->clip_x;
|
||||
gitem.clip_y = group->clip_y;
|
||||
gitem.clip_w = group->clip_w;
|
||||
gitem.clip_h = group->clip_h;
|
||||
gitem.start_layer = layer_count;
|
||||
gitem.num_layers = 0;
|
||||
|
||||
for(int l = 0; l < group->layers.len(); l++)
|
||||
{
|
||||
if(group->layers[l]->type == LAYERTYPE_TILES)
|
||||
{
|
||||
dbg_msg("editor", "saving tiles layer");
|
||||
LAYER_TILES *layer = (LAYER_TILES *)group->layers[l];
|
||||
layer->prepare_for_save();
|
||||
|
||||
MAPITEM_LAYER_TILEMAP item;
|
||||
item.version = 2;
|
||||
|
||||
item.layer.flags = layer->flags;
|
||||
item.layer.type = layer->type;
|
||||
|
||||
item.color.r = 255; // not in use right now
|
||||
item.color.g = 255;
|
||||
item.color.b = 255;
|
||||
item.color.a = 255;
|
||||
item.color_env = -1;
|
||||
item.color_env_offset = 0;
|
||||
|
||||
item.width = layer->width;
|
||||
item.height = layer->height;
|
||||
item.flags = layer->game;
|
||||
item.image = layer->image;
|
||||
item.data = datafile_add_data(df, layer->width*layer->height*sizeof(TILE), layer->tiles);
|
||||
datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
|
||||
|
||||
gitem.num_layers++;
|
||||
layer_count++;
|
||||
}
|
||||
else if(group->layers[l]->type == LAYERTYPE_QUADS)
|
||||
{
|
||||
dbg_msg("editor", "saving quads layer");
|
||||
LAYER_QUADS *layer = (LAYER_QUADS *)group->layers[l];
|
||||
if(layer->quads.len())
|
||||
{
|
||||
MAPITEM_LAYER_QUADS item;
|
||||
item.version = 1;
|
||||
item.layer.flags = layer->flags;
|
||||
item.layer.type = layer->type;
|
||||
item.image = layer->image;
|
||||
|
||||
// add the data
|
||||
item.num_quads = layer->quads.len();
|
||||
item.data = datafile_add_data_swapped(df, layer->quads.len()*sizeof(QUAD), layer->quads.getptr());
|
||||
datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
|
||||
|
||||
// clean up
|
||||
//mem_free(quads);
|
||||
|
||||
gitem.num_layers++;
|
||||
layer_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
datafile_add_item(df, MAPITEMTYPE_GROUP, g, sizeof(gitem), &gitem);
|
||||
}
|
||||
|
||||
// save envelopes
|
||||
int point_count = 0;
|
||||
for(int e = 0; e < envelopes.len(); e++)
|
||||
{
|
||||
MAPITEM_ENVELOPE item;
|
||||
item.version = 1;
|
||||
item.channels = envelopes[e]->channels;
|
||||
item.start_point = point_count;
|
||||
item.num_points = envelopes[e]->points.len();
|
||||
item.name = -1;
|
||||
|
||||
datafile_add_item(df, MAPITEMTYPE_ENVELOPE, e, sizeof(item), &item);
|
||||
point_count += item.num_points;
|
||||
}
|
||||
|
||||
// save points
|
||||
int totalsize = sizeof(ENVPOINT) * point_count;
|
||||
ENVPOINT *points = (ENVPOINT *)mem_alloc(totalsize, 1);
|
||||
point_count = 0;
|
||||
|
||||
for(int e = 0; e < envelopes.len(); e++)
|
||||
{
|
||||
int count = envelopes[e]->points.len();
|
||||
mem_copy(&points[point_count], envelopes[e]->points.getptr(), sizeof(ENVPOINT)*count);
|
||||
point_count += count;
|
||||
}
|
||||
|
||||
datafile_add_item(df, MAPITEMTYPE_ENVPOINTS, 0, totalsize, points);
|
||||
|
||||
// finish the data file
|
||||
datafile_finish(df);
|
||||
dbg_msg("editor", "done");
|
||||
|
||||
// send rcon.. if we can
|
||||
if(client_rcon_authed())
|
||||
{
|
||||
client_rcon("sv_map_reload 1");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EDITOR::load(const char *filename)
|
||||
{
|
||||
reset();
|
||||
return map.load(filename);
|
||||
}
|
||||
|
||||
int MAP::load(const char *filename)
|
||||
{
|
||||
DATAFILE *df = datafile_load(filename);
|
||||
if(!df)
|
||||
return 0;
|
||||
|
||||
clean();
|
||||
|
||||
// check version
|
||||
MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0);
|
||||
if(!item)
|
||||
{
|
||||
// import old map
|
||||
MAP old_mapstuff;
|
||||
editor.reset();
|
||||
editor_load_old(df, this);
|
||||
}
|
||||
else if(item->version == 1)
|
||||
{
|
||||
//editor.reset(false);
|
||||
|
||||
// load images
|
||||
{
|
||||
int start, num;
|
||||
datafile_get_type(df, MAPITEMTYPE_IMAGE, &start, &num);
|
||||
for(int i = 0; i < num; i++)
|
||||
{
|
||||
MAPITEM_IMAGE *item = (MAPITEM_IMAGE *)datafile_get_item(df, start+i, 0, 0);
|
||||
char *name = (char *)datafile_get_data(df, item->image_name);
|
||||
|
||||
// copy base info
|
||||
EDITOR_IMAGE *img = new EDITOR_IMAGE;
|
||||
img->external = item->external;
|
||||
|
||||
if(item->external)
|
||||
{
|
||||
char buf[256];
|
||||
sprintf(buf, "mapres/%s.png", name);
|
||||
|
||||
// load external
|
||||
EDITOR_IMAGE imginfo;
|
||||
if(gfx_load_png(&imginfo, buf))
|
||||
{
|
||||
*img = imginfo;
|
||||
img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO, 0);
|
||||
img->external = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
img->width = item->width;
|
||||
img->height = item->height;
|
||||
img->format = IMG_RGBA;
|
||||
|
||||
// copy image data
|
||||
void *data = datafile_get_data(df, item->image_data);
|
||||
img->data = mem_alloc(img->width*img->height*4, 1);
|
||||
mem_copy(img->data, data, img->width*img->height*4);
|
||||
img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO, 0);
|
||||
}
|
||||
|
||||
// copy image name
|
||||
if(name)
|
||||
strncpy(img->name, name, 128);
|
||||
|
||||
images.add(img);
|
||||
|
||||
// unload image
|
||||
datafile_unload_data(df, item->image_data);
|
||||
datafile_unload_data(df, item->image_name);
|
||||
}
|
||||
}
|
||||
|
||||
// load groups
|
||||
{
|
||||
int layers_start, layers_num;
|
||||
datafile_get_type(df, MAPITEMTYPE_LAYER, &layers_start, &layers_num);
|
||||
|
||||
int start, num;
|
||||
datafile_get_type(df, MAPITEMTYPE_GROUP, &start, &num);
|
||||
for(int g = 0; g < num; g++)
|
||||
{
|
||||
MAPITEM_GROUP *gitem = (MAPITEM_GROUP *)datafile_get_item(df, start+g, 0, 0);
|
||||
|
||||
if(gitem->version < 1 || gitem->version > MAPITEM_GROUP::CURRENT_VERSION)
|
||||
continue;
|
||||
|
||||
LAYERGROUP *group = new_group();
|
||||
group->parallax_x = gitem->parallax_x;
|
||||
group->parallax_y = gitem->parallax_y;
|
||||
group->offset_x = gitem->offset_x;
|
||||
group->offset_y = gitem->offset_y;
|
||||
|
||||
if(gitem->version >= 2)
|
||||
{
|
||||
group->use_clipping = gitem->use_clipping;
|
||||
group->clip_x = gitem->clip_x;
|
||||
group->clip_y = gitem->clip_y;
|
||||
group->clip_w = gitem->clip_w;
|
||||
group->clip_h = gitem->clip_h;
|
||||
}
|
||||
|
||||
for(int l = 0; l < gitem->num_layers; l++)
|
||||
{
|
||||
LAYER *layer = 0;
|
||||
MAPITEM_LAYER *layer_item = (MAPITEM_LAYER *)datafile_get_item(df, layers_start+gitem->start_layer+l, 0, 0);
|
||||
if(!layer_item)
|
||||
continue;
|
||||
|
||||
if(layer_item->type == LAYERTYPE_TILES)
|
||||
{
|
||||
MAPITEM_LAYER_TILEMAP *tilemap_item = (MAPITEM_LAYER_TILEMAP *)layer_item;
|
||||
LAYER_TILES *tiles = 0;
|
||||
|
||||
if(tilemap_item->flags&1)
|
||||
{
|
||||
tiles = new LAYER_GAME(tilemap_item->width, tilemap_item->height);
|
||||
make_game_layer(tiles);
|
||||
make_game_group(group);
|
||||
}
|
||||
else
|
||||
tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height);
|
||||
|
||||
layer = tiles;
|
||||
|
||||
group->add_layer(tiles);
|
||||
void *data = datafile_get_data(df, tilemap_item->data);
|
||||
tiles->image = tilemap_item->image;
|
||||
tiles->game = tilemap_item->flags&1;
|
||||
|
||||
mem_copy(tiles->tiles, data, tiles->width*tiles->height*sizeof(TILE));
|
||||
|
||||
if(tiles->game && tilemap_item->version == make_version(1, *tilemap_item))
|
||||
{
|
||||
for(int i = 0; i < tiles->width*tiles->height; i++)
|
||||
{
|
||||
if(tiles->tiles[i].index)
|
||||
tiles->tiles[i].index += ENTITY_OFFSET;
|
||||
}
|
||||
}
|
||||
|
||||
datafile_unload_data(df, tilemap_item->data);
|
||||
}
|
||||
else if(layer_item->type == LAYERTYPE_QUADS)
|
||||
{
|
||||
MAPITEM_LAYER_QUADS *quads_item = (MAPITEM_LAYER_QUADS *)layer_item;
|
||||
LAYER_QUADS *quads = new LAYER_QUADS;
|
||||
layer = quads;
|
||||
quads->image = quads_item->image;
|
||||
if(quads->image < -1 || quads->image >= images.len())
|
||||
quads->image = -1;
|
||||
void *data = datafile_get_data_swapped(df, quads_item->data);
|
||||
group->add_layer(quads);
|
||||
quads->quads.setsize(quads_item->num_quads);
|
||||
mem_copy(quads->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads);
|
||||
datafile_unload_data(df, quads_item->data);
|
||||
}
|
||||
|
||||
if(layer)
|
||||
layer->flags = layer_item->flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// load envelopes
|
||||
{
|
||||
ENVPOINT *points = 0;
|
||||
|
||||
{
|
||||
int start, num;
|
||||
datafile_get_type(df, MAPITEMTYPE_ENVPOINTS, &start, &num);
|
||||
if(num)
|
||||
points = (ENVPOINT *)datafile_get_item(df, start, 0, 0);
|
||||
}
|
||||
|
||||
int start, num;
|
||||
datafile_get_type(df, MAPITEMTYPE_ENVELOPE, &start, &num);
|
||||
for(int e = 0; e < num; e++)
|
||||
{
|
||||
MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)datafile_get_item(df, start+e, 0, 0);
|
||||
ENVELOPE *env = new ENVELOPE(item->channels);
|
||||
env->points.setsize(item->num_points);
|
||||
mem_copy(env->points.getptr(), &points[item->start_point], sizeof(ENVPOINT)*item->num_points);
|
||||
envelopes.add(env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
datafile_unload(df);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int modify_add_amount = 0;
|
||||
static void modify_add(int *index)
|
||||
{
|
||||
if(*index >= 0)
|
||||
*index += modify_add_amount;
|
||||
}
|
||||
|
||||
int EDITOR::append(const char *filename)
|
||||
{
|
||||
MAP new_map;
|
||||
int err;
|
||||
err = new_map.load(filename);
|
||||
if(err)
|
||||
return err;
|
||||
|
||||
// modify indecies
|
||||
modify_add_amount = map.images.len();
|
||||
new_map.modify_image_index(modify_add);
|
||||
|
||||
modify_add_amount = map.envelopes.len();
|
||||
new_map.modify_envelope_index(modify_add);
|
||||
|
||||
// transfer images
|
||||
for(int i = 0; i < new_map.images.len(); i++)
|
||||
map.images.add(new_map.images[i]);
|
||||
new_map.images.clear();
|
||||
|
||||
// transfer envelopes
|
||||
for(int i = 0; i < new_map.envelopes.len(); i++)
|
||||
map.envelopes.add(new_map.envelopes[i]);
|
||||
new_map.envelopes.clear();
|
||||
|
||||
// transfer groups
|
||||
|
||||
for(int i = 0; i < new_map.groups.len(); i++)
|
||||
{
|
||||
if(new_map.groups[i] == new_map.game_group)
|
||||
delete new_map.groups[i];
|
||||
else
|
||||
map.groups.add(new_map.groups[i]);
|
||||
}
|
||||
new_map.groups.clear();
|
||||
|
||||
// all done \o/
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "ed_editor.hpp"
|
||||
|
||||
|
||||
LAYER_GAME::LAYER_GAME(int w, int h)
|
||||
: LAYER_TILES(w, h)
|
||||
{
|
||||
type_name = "Game";
|
||||
game = 1;
|
||||
}
|
||||
|
||||
LAYER_GAME::~LAYER_GAME()
|
||||
{
|
||||
}
|
||||
|
||||
int LAYER_GAME::render_properties(RECT *toolbox)
|
||||
{
|
||||
int r = LAYER_TILES::render_properties(toolbox);
|
||||
image = -1;
|
||||
return r;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include "ed_editor.hpp"
|
||||
#include <game/generated/gc_data.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
|
||||
LAYER_QUADS::LAYER_QUADS()
|
||||
{
|
||||
type = LAYERTYPE_QUADS;
|
||||
type_name = "Quads";
|
||||
image = -1;
|
||||
}
|
||||
|
||||
LAYER_QUADS::~LAYER_QUADS()
|
||||
{
|
||||
}
|
||||
|
||||
static void envelope_eval(float time_offset, int env, float *channels)
|
||||
{
|
||||
if(env < 0 || env > editor.map.envelopes.len())
|
||||
{
|
||||
channels[0] = 0;
|
||||
channels[1] = 0;
|
||||
channels[2] = 0;
|
||||
channels[3] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
ENVELOPE *e = editor.map.envelopes[env];
|
||||
float t = editor.animate_time+time_offset;
|
||||
t *= editor.animate_speed;
|
||||
e->eval(t, channels);
|
||||
}
|
||||
|
||||
void LAYER_QUADS::render()
|
||||
{
|
||||
gfx_texture_set(-1);
|
||||
if(image >= 0 && image < editor.map.images.len())
|
||||
gfx_texture_set(editor.map.images[image]->tex_id);
|
||||
|
||||
render_quads(quads.getptr(), quads.len(), envelope_eval, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT);
|
||||
}
|
||||
|
||||
QUAD *LAYER_QUADS::new_quad()
|
||||
{
|
||||
QUAD *q = &quads[quads.add(QUAD())];
|
||||
|
||||
q->pos_env = -1;
|
||||
q->color_env = -1;
|
||||
q->pos_env_offset = 0;
|
||||
q->color_env_offset = 0;
|
||||
int x = 0, y = 0;
|
||||
q->points[0].x = x;
|
||||
q->points[0].y = y;
|
||||
q->points[1].x = x+64;
|
||||
q->points[1].y = y;
|
||||
q->points[2].x = x;
|
||||
q->points[2].y = y+64;
|
||||
q->points[3].x = x+64;
|
||||
q->points[3].y = y+64;
|
||||
|
||||
q->points[4].x = x+32; // pivot
|
||||
q->points[4].y = y+32;
|
||||
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
q->points[i].x <<= 10;
|
||||
q->points[i].y <<= 10;
|
||||
}
|
||||
|
||||
|
||||
q->texcoords[0].x = 0;
|
||||
q->texcoords[0].y = 0;
|
||||
|
||||
q->texcoords[1].x = 1<<10;
|
||||
q->texcoords[1].y = 0;
|
||||
|
||||
q->texcoords[2].x = 0;
|
||||
q->texcoords[2].y = 1<<10;
|
||||
|
||||
q->texcoords[3].x = 1<<10;
|
||||
q->texcoords[3].y = 1<<10;
|
||||
|
||||
q->colors[0].r = 255; q->colors[0].g = 255; q->colors[0].b = 255; q->colors[0].a = 255;
|
||||
q->colors[1].r = 255; q->colors[1].g = 255; q->colors[1].b = 255; q->colors[1].a = 255;
|
||||
q->colors[2].r = 255; q->colors[2].g = 255; q->colors[2].b = 255; q->colors[2].a = 255;
|
||||
q->colors[3].r = 255; q->colors[3].g = 255; q->colors[3].b = 255; q->colors[3].a = 255;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
void LAYER_QUADS::brush_selecting(RECT rect)
|
||||
{
|
||||
// draw selection rectangle
|
||||
gfx_texture_set(-1);
|
||||
gfx_lines_begin();
|
||||
gfx_lines_draw(rect.x, rect.y, rect.x+rect.w, rect.y);
|
||||
gfx_lines_draw(rect.x+rect.w, rect.y, rect.x+rect.w, rect.y+rect.h);
|
||||
gfx_lines_draw(rect.x+rect.w, rect.y+rect.h, rect.x, rect.y+rect.h);
|
||||
gfx_lines_draw(rect.x, rect.y+rect.h, rect.x, rect.y);
|
||||
gfx_lines_end();
|
||||
}
|
||||
|
||||
int LAYER_QUADS::brush_grab(LAYERGROUP *brush, RECT rect)
|
||||
{
|
||||
// create new layers
|
||||
LAYER_QUADS *grabbed = new LAYER_QUADS();
|
||||
grabbed->image = image;
|
||||
brush->add_layer(grabbed);
|
||||
|
||||
//dbg_msg("", "%f %f %f %f", rect.x, rect.y, rect.w, rect.h);
|
||||
for(int i = 0; i < quads.len(); i++)
|
||||
{
|
||||
QUAD *q = &quads[i];
|
||||
float px = fx2f(q->points[4].x);
|
||||
float py = fx2f(q->points[4].y);
|
||||
|
||||
if(px > rect.x && px < rect.x+rect.w && py > rect.y && py < rect.y+rect.h)
|
||||
{
|
||||
dbg_msg("", "grabbed one");
|
||||
QUAD n;
|
||||
n = *q;
|
||||
|
||||
for(int p = 0; p < 5; p++)
|
||||
{
|
||||
n.points[p].x -= f2fx(rect.x);
|
||||
n.points[p].y -= f2fx(rect.y);
|
||||
}
|
||||
|
||||
grabbed->quads.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
return grabbed->quads.len()?1:0;
|
||||
}
|
||||
|
||||
void LAYER_QUADS::brush_place(LAYER *brush, float wx, float wy)
|
||||
{
|
||||
LAYER_QUADS *l = (LAYER_QUADS *)brush;
|
||||
for(int i = 0; i < l->quads.len(); i++)
|
||||
{
|
||||
QUAD n = l->quads[i];
|
||||
|
||||
for(int p = 0; p < 5; p++)
|
||||
{
|
||||
n.points[p].x += f2fx(wx);
|
||||
n.points[p].y += f2fx(wy);
|
||||
}
|
||||
|
||||
quads.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
void LAYER_QUADS::brush_flip_x()
|
||||
{
|
||||
}
|
||||
|
||||
void LAYER_QUADS::brush_flip_y()
|
||||
{
|
||||
}
|
||||
|
||||
void rotate(vec2 *center, vec2 *point, float rotation)
|
||||
{
|
||||
float x = point->x - center->x;
|
||||
float y = point->y - center->y;
|
||||
point->x = x * cosf(rotation) - y * sinf(rotation) + center->x;
|
||||
point->y = x * sinf(rotation) + y * cosf(rotation) + center->y;
|
||||
}
|
||||
|
||||
void LAYER_QUADS::brush_rotate(float amount)
|
||||
{
|
||||
vec2 center;
|
||||
get_size(¢er.x, ¢er.y);
|
||||
center.x /= 2;
|
||||
center.y /= 2;
|
||||
|
||||
for(int i = 0; i < quads.len(); i++)
|
||||
{
|
||||
QUAD *q = &quads[i];
|
||||
|
||||
for(int p = 0; p < 5; p++)
|
||||
{
|
||||
vec2 pos(fx2f(q->points[p].x), fx2f(q->points[p].y));
|
||||
rotate(¢er, &pos, amount);
|
||||
q->points[p].x = f2fx(pos.x);
|
||||
q->points[p].y = f2fx(pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LAYER_QUADS::get_size(float *w, float *h)
|
||||
{
|
||||
*w = 0; *h = 0;
|
||||
|
||||
for(int i = 0; i < quads.len(); i++)
|
||||
{
|
||||
for(int p = 0; p < 5; p++)
|
||||
{
|
||||
*w = max(*w, fx2f(quads[i].points[p].x));
|
||||
*h = max(*h, fx2f(quads[i].points[p].y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int selected_points;
|
||||
|
||||
int LAYER_QUADS::render_properties(RECT *toolbox)
|
||||
{
|
||||
// layer props
|
||||
enum
|
||||
{
|
||||
PROP_IMAGE=0,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
PROPERTY props[] = {
|
||||
{"Image", image, PROPTYPE_IMAGE, -1, 0},
|
||||
{0},
|
||||
};
|
||||
|
||||
static int ids[NUM_PROPS] = {0};
|
||||
int new_val = 0;
|
||||
int prop = editor.do_properties(toolbox, props, ids, &new_val);
|
||||
|
||||
if(prop == PROP_IMAGE)
|
||||
{
|
||||
if(new_val >= 0)
|
||||
image = new_val%editor.map.images.len();
|
||||
else
|
||||
image = -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void LAYER_QUADS::modify_image_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
func(&image);
|
||||
}
|
||||
|
||||
void LAYER_QUADS::modify_envelope_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
for(int i = 0; i < quads.len(); i++)
|
||||
{
|
||||
func(&quads[i].pos_env);
|
||||
func(&quads[i].color_env);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <game/generated/gc_data.hpp>
|
||||
#include <game/client/render.hpp>
|
||||
#include "ed_editor.hpp"
|
||||
|
||||
LAYER_TILES::LAYER_TILES(int w, int h)
|
||||
{
|
||||
type = LAYERTYPE_TILES;
|
||||
type_name = "Tiles";
|
||||
width = w;
|
||||
height = h;
|
||||
image = -1;
|
||||
tex_id = -1;
|
||||
game = 0;
|
||||
|
||||
tiles = new TILE[width*height];
|
||||
mem_zero(tiles, width*height*sizeof(TILE));
|
||||
}
|
||||
|
||||
LAYER_TILES::~LAYER_TILES()
|
||||
{
|
||||
delete [] tiles;
|
||||
}
|
||||
|
||||
void LAYER_TILES::prepare_for_save()
|
||||
{
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
tiles[y*width+x].flags &= TILEFLAG_VFLIP|TILEFLAG_HFLIP;
|
||||
|
||||
if(image != -1)
|
||||
{
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
tiles[y*width+x].flags |= editor.map.images[image]->tileflags[tiles[y*width+x].index];
|
||||
}
|
||||
}
|
||||
|
||||
void LAYER_TILES::make_palette()
|
||||
{
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
tiles[y*width+x].index = y*16+x;
|
||||
}
|
||||
|
||||
void LAYER_TILES::render()
|
||||
{
|
||||
if(image >= 0 && image < editor.map.images.len())
|
||||
tex_id = editor.map.images[image]->tex_id;
|
||||
gfx_texture_set(tex_id);
|
||||
render_tilemap(tiles, width, height, 32.0f, vec4(1,1,1,1), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT);
|
||||
}
|
||||
|
||||
int LAYER_TILES::convert_x(float x) const { return (int)(x/32.0f); }
|
||||
int LAYER_TILES::convert_y(float y) const { return (int)(y/32.0f); }
|
||||
|
||||
void LAYER_TILES::convert(RECT rect, RECTi *out)
|
||||
{
|
||||
out->x = convert_x(rect.x);
|
||||
out->y = convert_y(rect.y);
|
||||
out->w = convert_x(rect.x+rect.w+31) - out->x;
|
||||
out->h = convert_y(rect.y+rect.h+31) - out->y;
|
||||
}
|
||||
|
||||
void LAYER_TILES::snap(RECT *rect)
|
||||
{
|
||||
RECTi out;
|
||||
convert(*rect, &out);
|
||||
rect->x = out.x*32.0f;
|
||||
rect->y = out.y*32.0f;
|
||||
rect->w = out.w*32.0f;
|
||||
rect->h = out.h*32.0f;
|
||||
}
|
||||
|
||||
void LAYER_TILES::clamp(RECTi *rect)
|
||||
{
|
||||
if(rect->x < 0)
|
||||
{
|
||||
rect->w += rect->x;
|
||||
rect->x = 0;
|
||||
}
|
||||
|
||||
if(rect->y < 0)
|
||||
{
|
||||
rect->h += rect->y;
|
||||
rect->y = 0;
|
||||
}
|
||||
|
||||
if(rect->x+rect->w > width)
|
||||
rect->w = width-rect->x;
|
||||
|
||||
if(rect->y+rect->h > height)
|
||||
rect->h = height-rect->y;
|
||||
|
||||
if(rect->h < 0)
|
||||
rect->h = 0;
|
||||
if(rect->w < 0)
|
||||
rect->w = 0;
|
||||
}
|
||||
|
||||
void LAYER_TILES::brush_selecting(RECT rect)
|
||||
{
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_setcolor(1,1,1,0.4f);
|
||||
snap(&rect);
|
||||
gfx_quads_drawTL(rect.x, rect.y, rect.w, rect.h);
|
||||
gfx_quads_end();
|
||||
char buf[16];
|
||||
str_format(buf, sizeof(buf), "%d,%d", convert_x(rect.w), convert_y(rect.h));
|
||||
gfx_text(0, rect.x+3.0f, rect.y+3.0f, 15.0f*editor.world_zoom, buf, -1);
|
||||
}
|
||||
|
||||
int LAYER_TILES::brush_grab(LAYERGROUP *brush, RECT rect)
|
||||
{
|
||||
RECTi r;
|
||||
convert(rect, &r);
|
||||
clamp(&r);
|
||||
|
||||
if(!r.w || !r.h)
|
||||
return 0;
|
||||
|
||||
// create new layers
|
||||
LAYER_TILES *grabbed = new LAYER_TILES(r.w, r.h);
|
||||
grabbed->tex_id = tex_id;
|
||||
grabbed->image = image;
|
||||
brush->add_layer(grabbed);
|
||||
|
||||
// copy the tiles
|
||||
for(int y = 0; y < r.h; y++)
|
||||
for(int x = 0; x < r.w; x++)
|
||||
grabbed->tiles[y*grabbed->width+x] = tiles[(r.y+y)*width+(r.x+x)];
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void LAYER_TILES::brush_draw(LAYER *brush, float wx, float wy)
|
||||
{
|
||||
if(readonly)
|
||||
return;
|
||||
|
||||
//
|
||||
LAYER_TILES *l = (LAYER_TILES *)brush;
|
||||
int sx = convert_x(wx);
|
||||
int sy = convert_y(wy);
|
||||
|
||||
for(int y = 0; y < l->height; y++)
|
||||
for(int x = 0; x < l->width; x++)
|
||||
{
|
||||
int fx = x+sx;
|
||||
int fy = y+sy;
|
||||
if(fx<0 || fx >= width || fy < 0 || fy >= height)
|
||||
continue;
|
||||
|
||||
tiles[fy*width+fx] = l->tiles[y*l->width+x];
|
||||
}
|
||||
}
|
||||
|
||||
void LAYER_TILES::brush_flip_x()
|
||||
{
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width/2; x++)
|
||||
{
|
||||
TILE tmp = tiles[y*width+x];
|
||||
tiles[y*width+x] = tiles[y*width+width-1-x];
|
||||
tiles[y*width+width-1-x] = tmp;
|
||||
}
|
||||
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
tiles[y*width+x].flags ^= TILEFLAG_VFLIP;
|
||||
}
|
||||
|
||||
void LAYER_TILES::brush_flip_y()
|
||||
{
|
||||
for(int y = 0; y < height/2; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
{
|
||||
TILE tmp = tiles[y*width+x];
|
||||
tiles[y*width+x] = tiles[(height-1-y)*width+x];
|
||||
tiles[(height-1-y)*width+x] = tmp;
|
||||
}
|
||||
|
||||
for(int y = 0; y < height; y++)
|
||||
for(int x = 0; x < width; x++)
|
||||
tiles[y*width+x].flags ^= TILEFLAG_HFLIP;
|
||||
}
|
||||
|
||||
void LAYER_TILES::resize(int new_w, int new_h)
|
||||
{
|
||||
TILE *new_data = new TILE[new_w*new_h];
|
||||
mem_zero(new_data, new_w*new_h*sizeof(TILE));
|
||||
|
||||
// copy old data
|
||||
for(int y = 0; y < min(new_h, height); y++)
|
||||
mem_copy(&new_data[y*new_w], &tiles[y*width], min(width, new_w)*sizeof(TILE));
|
||||
|
||||
// replace old
|
||||
delete [] tiles;
|
||||
tiles = new_data;
|
||||
width = new_w;
|
||||
height = new_h;
|
||||
}
|
||||
|
||||
|
||||
int LAYER_TILES::render_properties(RECT *toolbox)
|
||||
{
|
||||
RECT button;
|
||||
ui_hsplit_b(toolbox, 12.0f, toolbox, &button);
|
||||
bool in_gamegroup = editor.map.game_group->layers.find(this) != -1;
|
||||
if(editor.map.game_layer == this)
|
||||
in_gamegroup = false;
|
||||
static int colcl_button = 0;
|
||||
if(do_editor_button(&colcl_button, "Clear Collision", in_gamegroup?0:-1, &button, draw_editor_button, 0, "Removes collision from this layer"))
|
||||
{
|
||||
LAYER_TILES *gl = editor.map.game_layer;
|
||||
int w = min(gl->width, width);
|
||||
int h = min(gl->height, height);
|
||||
for(int y = 0; y < h; y++)
|
||||
for(int x = 0; x < w; x++)
|
||||
{
|
||||
if(gl->tiles[y*gl->width+x].index <= TILE_SOLID)
|
||||
if(tiles[y*width+x].index)
|
||||
gl->tiles[y*gl->width+x].index = TILE_AIR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
static int col_button = 0;
|
||||
ui_hsplit_b(toolbox, 5.0f, toolbox, &button);
|
||||
ui_hsplit_b(toolbox, 12.0f, toolbox, &button);
|
||||
if(do_editor_button(&col_button, "Make Collision", in_gamegroup?0:-1, &button, draw_editor_button, 0, "Constructs collision from this layer"))
|
||||
{
|
||||
LAYER_TILES *gl = editor.map.game_layer;
|
||||
int w = min(gl->width, width);
|
||||
int h = min(gl->height, height);
|
||||
for(int y = 0; y < h; y++)
|
||||
for(int x = 0; x < w; x++)
|
||||
{
|
||||
if(gl->tiles[y*gl->width+x].index <= TILE_SOLID)
|
||||
gl->tiles[y*gl->width+x].index = tiles[y*width+x].index?TILE_SOLID:TILE_AIR;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_WIDTH=0,
|
||||
PROP_HEIGHT,
|
||||
PROP_IMAGE,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
PROPERTY props[] = {
|
||||
{"Width", width, PROPTYPE_INT_STEP, 1, 1000000000},
|
||||
{"Height", height, PROPTYPE_INT_STEP, 1, 1000000000},
|
||||
{"Image", image, PROPTYPE_IMAGE, 0, 0},
|
||||
{0},
|
||||
};
|
||||
|
||||
if(editor.map.game_layer == this) // remove the image from the selection if this is the game layer
|
||||
props[2].name = 0;
|
||||
|
||||
static int ids[NUM_PROPS] = {0};
|
||||
int new_val = 0;
|
||||
int prop = editor.do_properties(toolbox, props, ids, &new_val);
|
||||
|
||||
if(prop == PROP_WIDTH && new_val > 1)
|
||||
resize(new_val, height);
|
||||
else if(prop == PROP_HEIGHT && new_val > 1)
|
||||
resize(width, new_val);
|
||||
else if(prop == PROP_IMAGE)
|
||||
{
|
||||
if (new_val == -1)
|
||||
{
|
||||
tex_id = -1;
|
||||
image = -1;
|
||||
}
|
||||
else
|
||||
image = new_val%editor.map.images.len();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void LAYER_TILES::modify_image_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
func(&image);
|
||||
}
|
||||
|
||||
void LAYER_TILES::modify_envelope_index(INDEX_MODIFY_FUNC func)
|
||||
{
|
||||
}
|
||||
424
project/jni/application/teeworlds/src/game/editor/ed_popups.cpp
Normal file
424
project/jni/application/teeworlds/src/game/editor/ed_popups.cpp
Normal file
@@ -0,0 +1,424 @@
|
||||
#include <stdio.h>
|
||||
#include "ed_editor.hpp"
|
||||
|
||||
|
||||
// popup menu handling
|
||||
static struct
|
||||
{
|
||||
RECT rect;
|
||||
void *id;
|
||||
int (*func)(RECT rect);
|
||||
int is_menu;
|
||||
void *extra;
|
||||
} ui_popups[8];
|
||||
|
||||
static int ui_num_popups = 0;
|
||||
|
||||
void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra)
|
||||
{
|
||||
dbg_msg("", "invoked");
|
||||
ui_popups[ui_num_popups].id = id;
|
||||
ui_popups[ui_num_popups].is_menu = flags;
|
||||
ui_popups[ui_num_popups].rect.x = x;
|
||||
ui_popups[ui_num_popups].rect.y = y;
|
||||
ui_popups[ui_num_popups].rect.w = w;
|
||||
ui_popups[ui_num_popups].rect.h = h;
|
||||
ui_popups[ui_num_popups].func = func;
|
||||
ui_popups[ui_num_popups].extra = extra;
|
||||
ui_num_popups++;
|
||||
}
|
||||
|
||||
void ui_do_popup_menu()
|
||||
{
|
||||
for(int i = 0; i < ui_num_popups; i++)
|
||||
{
|
||||
bool inside = ui_mouse_inside(&ui_popups[i].rect);
|
||||
ui_set_hot_item(&ui_popups[i].id);
|
||||
|
||||
if(ui_active_item() == &ui_popups[i].id)
|
||||
{
|
||||
if(!ui_mouse_button(0))
|
||||
{
|
||||
if(!inside)
|
||||
ui_num_popups--;
|
||||
ui_set_active_item(0);
|
||||
}
|
||||
}
|
||||
else if(ui_hot_item() == &ui_popups[i].id)
|
||||
{
|
||||
if(ui_mouse_button(0))
|
||||
ui_set_active_item(&ui_popups[i].id);
|
||||
}
|
||||
|
||||
int corners = CORNER_ALL;
|
||||
if(ui_popups[i].is_menu)
|
||||
corners = CORNER_R|CORNER_B;
|
||||
|
||||
RECT r = ui_popups[i].rect;
|
||||
ui_draw_rect(&r, vec4(0.5f,0.5f,0.5f,0.75f), corners, 3.0f);
|
||||
ui_margin(&r, 1.0f, &r);
|
||||
ui_draw_rect(&r, vec4(0,0,0,0.75f), corners, 3.0f);
|
||||
ui_margin(&r, 4.0f, &r);
|
||||
|
||||
if(ui_popups[i].func(r))
|
||||
ui_num_popups--;
|
||||
|
||||
if(inp_key_down(KEY_ESCAPE))
|
||||
ui_num_popups--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int popup_group(RECT view)
|
||||
{
|
||||
// remove group button
|
||||
RECT button;
|
||||
ui_hsplit_b(&view, 12.0f, &view, &button);
|
||||
static int delete_button = 0;
|
||||
|
||||
// don't allow deletion of game group
|
||||
if(editor.map.game_group != editor.get_selected_group() &&
|
||||
do_editor_button(&delete_button, "Delete Group", 0, &button, draw_editor_button, 0, "Delete group"))
|
||||
{
|
||||
editor.map.delete_group(editor.selected_group);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// new tile layer
|
||||
ui_hsplit_b(&view, 10.0f, &view, &button);
|
||||
ui_hsplit_b(&view, 12.0f, &view, &button);
|
||||
static int new_quad_layer_button = 0;
|
||||
if(do_editor_button(&new_quad_layer_button, "Add Quads Layer", 0, &button, draw_editor_button, 0, "Creates a new quad layer"))
|
||||
{
|
||||
LAYER *l = new LAYER_QUADS;
|
||||
editor.map.groups[editor.selected_group]->add_layer(l);
|
||||
editor.selected_layer = editor.map.groups[editor.selected_group]->layers.len()-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// new quad layer
|
||||
ui_hsplit_b(&view, 5.0f, &view, &button);
|
||||
ui_hsplit_b(&view, 12.0f, &view, &button);
|
||||
static int new_tile_layer_button = 0;
|
||||
if(do_editor_button(&new_tile_layer_button, "Add Tile Layer", 0, &button, draw_editor_button, 0, "Creates a new tile layer"))
|
||||
{
|
||||
LAYER *l = new LAYER_TILES(50, 50);
|
||||
editor.map.groups[editor.selected_group]->add_layer(l);
|
||||
editor.selected_layer = editor.map.groups[editor.selected_group]->layers.len()-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_ORDER=0,
|
||||
PROP_POS_X,
|
||||
PROP_POS_Y,
|
||||
PROP_PARA_X,
|
||||
PROP_PARA_Y,
|
||||
PROP_USE_CLIPPING,
|
||||
PROP_CLIP_X,
|
||||
PROP_CLIP_Y,
|
||||
PROP_CLIP_W,
|
||||
PROP_CLIP_H,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
PROPERTY props[] = {
|
||||
{"Order", editor.selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
|
||||
{"Pos X", -editor.map.groups[editor.selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Pos Y", -editor.map.groups[editor.selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Para X", editor.map.groups[editor.selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Para Y", editor.map.groups[editor.selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
|
||||
{"Use Clipping", editor.map.groups[editor.selected_group]->use_clipping, PROPTYPE_BOOL, 0, 1},
|
||||
{"Clip X", editor.map.groups[editor.selected_group]->clip_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Clip Y", editor.map.groups[editor.selected_group]->clip_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Clip W", editor.map.groups[editor.selected_group]->clip_w, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Clip H", editor.map.groups[editor.selected_group]->clip_h, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{0},
|
||||
};
|
||||
|
||||
static int ids[NUM_PROPS] = {0};
|
||||
int new_val = 0;
|
||||
|
||||
// cut the properties that isn't needed
|
||||
if(editor.get_selected_group()->game_group)
|
||||
props[PROP_POS_X].name = 0;
|
||||
|
||||
int prop = editor.do_properties(&view, props, ids, &new_val);
|
||||
if(prop == PROP_ORDER)
|
||||
editor.selected_group = editor.map.swap_groups(editor.selected_group, new_val);
|
||||
|
||||
// these can not be changed on the game group
|
||||
if(!editor.get_selected_group()->game_group)
|
||||
{
|
||||
if(prop == PROP_PARA_X) editor.map.groups[editor.selected_group]->parallax_x = new_val;
|
||||
else if(prop == PROP_PARA_Y) editor.map.groups[editor.selected_group]->parallax_y = new_val;
|
||||
else if(prop == PROP_POS_X) editor.map.groups[editor.selected_group]->offset_x = -new_val;
|
||||
else if(prop == PROP_POS_Y) editor.map.groups[editor.selected_group]->offset_y = -new_val;
|
||||
else if(prop == PROP_USE_CLIPPING) editor.map.groups[editor.selected_group]->use_clipping = new_val;
|
||||
else if(prop == PROP_CLIP_X) editor.map.groups[editor.selected_group]->clip_x = new_val;
|
||||
else if(prop == PROP_CLIP_Y) editor.map.groups[editor.selected_group]->clip_y = new_val;
|
||||
else if(prop == PROP_CLIP_W) editor.map.groups[editor.selected_group]->clip_w = new_val;
|
||||
else if(prop == PROP_CLIP_H) editor.map.groups[editor.selected_group]->clip_h = new_val;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int popup_layer(RECT view)
|
||||
{
|
||||
// remove layer button
|
||||
RECT button;
|
||||
ui_hsplit_b(&view, 12.0f, &view, &button);
|
||||
static int delete_button = 0;
|
||||
|
||||
// don't allow deletion of game layer
|
||||
if(editor.map.game_layer != editor.get_selected_layer(0) &&
|
||||
do_editor_button(&delete_button, "Delete Layer", 0, &button, draw_editor_button, 0, "Deletes the layer"))
|
||||
{
|
||||
editor.map.groups[editor.selected_group]->delete_layer(editor.selected_layer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ui_hsplit_b(&view, 10.0f, &view, 0);
|
||||
|
||||
LAYERGROUP *current_group = editor.map.groups[editor.selected_group];
|
||||
LAYER *current_layer = editor.get_selected_layer(0);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_GROUP=0,
|
||||
PROP_ORDER,
|
||||
PROP_HQ,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
PROPERTY props[] = {
|
||||
{"Group", editor.selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
|
||||
{"Order", editor.selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()},
|
||||
{"Detail", current_layer->flags&LAYERFLAG_DETAIL, PROPTYPE_BOOL, 0, 1},
|
||||
{0},
|
||||
};
|
||||
|
||||
static int ids[NUM_PROPS] = {0};
|
||||
int new_val = 0;
|
||||
int prop = editor.do_properties(&view, props, ids, &new_val);
|
||||
|
||||
if(prop == PROP_ORDER)
|
||||
editor.selected_layer = current_group->swap_layers(editor.selected_layer, new_val);
|
||||
else if(prop == PROP_GROUP && current_layer->type != LAYERTYPE_GAME)
|
||||
{
|
||||
if(new_val >= 0 && new_val < editor.map.groups.len())
|
||||
{
|
||||
current_group->layers.remove(current_layer);
|
||||
editor.map.groups[new_val]->layers.add(current_layer);
|
||||
editor.selected_group = new_val;
|
||||
editor.selected_layer = editor.map.groups[new_val]->layers.len()-1;
|
||||
}
|
||||
}
|
||||
else if(prop == PROP_HQ)
|
||||
{
|
||||
current_layer->flags &= ~LAYERFLAG_DETAIL;
|
||||
if(new_val)
|
||||
current_layer->flags |= LAYERFLAG_DETAIL;
|
||||
}
|
||||
|
||||
return current_layer->render_properties(&view);
|
||||
}
|
||||
|
||||
int popup_quad(RECT view)
|
||||
{
|
||||
QUAD *quad = editor.get_selected_quad();
|
||||
|
||||
RECT button;
|
||||
|
||||
// delete button
|
||||
ui_hsplit_b(&view, 12.0f, &view, &button);
|
||||
static int delete_button = 0;
|
||||
if(do_editor_button(&delete_button, "Delete", 0, &button, draw_editor_button, 0, "Deletes the current quad"))
|
||||
{
|
||||
LAYER_QUADS *layer = (LAYER_QUADS *)editor.get_selected_layer_type(0, LAYERTYPE_QUADS);
|
||||
if(layer)
|
||||
{
|
||||
layer->quads.removebyindex(editor.selected_quad);
|
||||
editor.selected_quad--;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// square button
|
||||
ui_hsplit_b(&view, 10.0f, &view, &button);
|
||||
ui_hsplit_b(&view, 12.0f, &view, &button);
|
||||
static int sq_button = 0;
|
||||
if(do_editor_button(&sq_button, "Square", 0, &button, draw_editor_button, 0, "Squares the current quad"))
|
||||
{
|
||||
int top = quad->points[0].y;
|
||||
int left = quad->points[0].x;
|
||||
int bottom = quad->points[0].y;
|
||||
int right = quad->points[0].x;
|
||||
|
||||
for(int k = 1; k < 4; k++)
|
||||
{
|
||||
if(quad->points[k].y < top) top = quad->points[k].y;
|
||||
if(quad->points[k].x < left) left = quad->points[k].x;
|
||||
if(quad->points[k].y > bottom) bottom = quad->points[k].y;
|
||||
if(quad->points[k].x > right) right = quad->points[k].x;
|
||||
}
|
||||
|
||||
quad->points[0].x = left; quad->points[0].y = top;
|
||||
quad->points[1].x = right; quad->points[1].y = top;
|
||||
quad->points[2].x = left; quad->points[2].y = bottom;
|
||||
quad->points[3].x = right; quad->points[3].y = bottom;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_POS_ENV=0,
|
||||
PROP_POS_ENV_OFFSET,
|
||||
PROP_COLOR_ENV,
|
||||
PROP_COLOR_ENV_OFFSET,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
PROPERTY props[] = {
|
||||
{"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
|
||||
{"Pos. TO", quad->pos_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
{"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
|
||||
{"Color TO", quad->color_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
|
||||
|
||||
{0},
|
||||
};
|
||||
|
||||
static int ids[NUM_PROPS] = {0};
|
||||
int new_val = 0;
|
||||
int prop = editor.do_properties(&view, props, ids, &new_val);
|
||||
|
||||
if(prop == PROP_POS_ENV) quad->pos_env = clamp(new_val, -1, editor.map.envelopes.len()-1);
|
||||
if(prop == PROP_POS_ENV_OFFSET) quad->pos_env_offset = new_val;
|
||||
if(prop == PROP_COLOR_ENV) quad->color_env = clamp(new_val, -1, editor.map.envelopes.len()-1);
|
||||
if(prop == PROP_COLOR_ENV_OFFSET) quad->color_env_offset = new_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int popup_point(RECT view)
|
||||
{
|
||||
QUAD *quad = editor.get_selected_quad();
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_COLOR=0,
|
||||
NUM_PROPS,
|
||||
};
|
||||
|
||||
int color = 0;
|
||||
|
||||
for(int v = 0; v < 4; v++)
|
||||
{
|
||||
if(editor.selected_points&(1<<v))
|
||||
{
|
||||
color = 0;
|
||||
color |= quad->colors[v].r<<24;
|
||||
color |= quad->colors[v].g<<16;
|
||||
color |= quad->colors[v].b<<8;
|
||||
color |= quad->colors[v].a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PROPERTY props[] = {
|
||||
{"Color", color, PROPTYPE_COLOR, -1, editor.map.envelopes.len()},
|
||||
{0},
|
||||
};
|
||||
|
||||
static int ids[NUM_PROPS] = {0};
|
||||
int new_val = 0;
|
||||
int prop = editor.do_properties(&view, props, ids, &new_val);
|
||||
if(prop == PROP_COLOR)
|
||||
{
|
||||
for(int v = 0; v < 4; v++)
|
||||
{
|
||||
if(editor.selected_points&(1<<v))
|
||||
{
|
||||
color = 0;
|
||||
quad->colors[v].r = (new_val>>24)&0xff;
|
||||
quad->colors[v].g = (new_val>>16)&0xff;
|
||||
quad->colors[v].b = (new_val>>8)&0xff;
|
||||
quad->colors[v].a = new_val&0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int select_image_selected = -100;
|
||||
static int select_image_current = -100;
|
||||
|
||||
int popup_select_image(RECT view)
|
||||
{
|
||||
RECT buttonbar, imageview;
|
||||
ui_vsplit_l(&view, 80.0f, &buttonbar, &view);
|
||||
ui_margin(&view, 10.0f, &imageview);
|
||||
|
||||
int show_image = select_image_current;
|
||||
|
||||
for(int i = -1; i < editor.map.images.len(); i++)
|
||||
{
|
||||
RECT button;
|
||||
ui_hsplit_t(&buttonbar, 12.0f, &button, &buttonbar);
|
||||
ui_hsplit_t(&buttonbar, 2.0f, 0, &buttonbar);
|
||||
|
||||
if(ui_mouse_inside(&button))
|
||||
show_image = i;
|
||||
|
||||
if(i == -1)
|
||||
{
|
||||
if(do_editor_button(&editor.map.images[i], "None", i==select_image_current, &button, draw_editor_button_menuitem, 0, 0))
|
||||
select_image_selected = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(do_editor_button(&editor.map.images[i], editor.map.images[i]->name, i==select_image_current, &button, draw_editor_button_menuitem, 0, 0))
|
||||
select_image_selected = i;
|
||||
}
|
||||
}
|
||||
|
||||
if(show_image >= 0 && show_image < editor.map.images.len())
|
||||
gfx_texture_set(editor.map.images[show_image]->tex_id);
|
||||
else
|
||||
gfx_texture_set(-1);
|
||||
gfx_quads_begin();
|
||||
gfx_quads_drawTL(imageview.x, imageview.y, imageview.w, imageview.h);
|
||||
gfx_quads_end();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void popup_select_image_invoke(int current, float x, float y)
|
||||
{
|
||||
static int select_image_popup_id = 0;
|
||||
select_image_selected = -100;
|
||||
select_image_current = current;
|
||||
ui_invoke_popup_menu(&select_image_popup_id, 0, x, y, 400, 300, popup_select_image);
|
||||
}
|
||||
|
||||
int popup_select_image_result()
|
||||
{
|
||||
if(select_image_selected == -100)
|
||||
return -100;
|
||||
|
||||
select_image_current = select_image_selected;
|
||||
select_image_selected = -100;
|
||||
return select_image_current;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
514
project/jni/application/teeworlds/src/game/gamecore.cpp
Normal file
514
project/jni/application/teeworlds/src/game/gamecore.cpp
Normal file
@@ -0,0 +1,514 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <string.h>
|
||||
#include "gamecore.hpp"
|
||||
|
||||
const char *TUNING_PARAMS::names[] =
|
||||
{
|
||||
#define MACRO_TUNING_PARAM(name,value) #name,
|
||||
#include "tuning.hpp"
|
||||
#undef MACRO_TUNING_PARAM
|
||||
};
|
||||
|
||||
|
||||
bool TUNING_PARAMS::set(int index, float value)
|
||||
{
|
||||
if(index < 0 || index >= num())
|
||||
return false;
|
||||
((tune_param *)this)[index] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TUNING_PARAMS::get(int index, float *value)
|
||||
{
|
||||
if(index < 0 || index >= num())
|
||||
return false;
|
||||
*value = (float)((tune_param *)this)[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TUNING_PARAMS::set(const char *name, float value)
|
||||
{
|
||||
for(int i = 0; i < num(); i++)
|
||||
if(strcmp(name, names[i]) == 0)
|
||||
return set(i, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TUNING_PARAMS::get(const char *name, float *value)
|
||||
{
|
||||
for(int i = 0; i < num(); i++)
|
||||
if(strcmp(name, names[i]) == 0)
|
||||
return get(i, value);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: OPT: rewrite this smarter!
|
||||
void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces)
|
||||
{
|
||||
if(bounces)
|
||||
*bounces = 0;
|
||||
|
||||
vec2 pos = *inout_pos;
|
||||
vec2 vel = *inout_vel;
|
||||
if(col_check_point(pos + vel))
|
||||
{
|
||||
int affected = 0;
|
||||
if(col_check_point(pos.x + vel.x, pos.y))
|
||||
{
|
||||
inout_vel->x *= -elasticity;
|
||||
if(bounces)
|
||||
(*bounces)++;
|
||||
affected++;
|
||||
}
|
||||
|
||||
if(col_check_point(pos.x, pos.y + vel.y))
|
||||
{
|
||||
inout_vel->y *= -elasticity;
|
||||
if(bounces)
|
||||
(*bounces)++;
|
||||
affected++;
|
||||
}
|
||||
|
||||
if(affected == 0)
|
||||
{
|
||||
inout_vel->x *= -elasticity;
|
||||
inout_vel->y *= -elasticity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*inout_pos = pos + vel;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_box(vec2 pos, vec2 size)
|
||||
{
|
||||
size *= 0.5f;
|
||||
if(col_check_point(pos.x-size.x, pos.y-size.y))
|
||||
return true;
|
||||
if(col_check_point(pos.x+size.x, pos.y-size.y))
|
||||
return true;
|
||||
if(col_check_point(pos.x-size.x, pos.y+size.y))
|
||||
return true;
|
||||
if(col_check_point(pos.x+size.x, pos.y+size.y))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
|
||||
{
|
||||
// do the move
|
||||
vec2 pos = *inout_pos;
|
||||
vec2 vel = *inout_vel;
|
||||
|
||||
float distance = length(vel);
|
||||
int max = (int)distance;
|
||||
|
||||
if(distance > 0.00001f)
|
||||
{
|
||||
//vec2 old_pos = pos;
|
||||
float fraction = 1.0f/(float)(max+1);
|
||||
for(int i = 0; i <= max; i++)
|
||||
{
|
||||
//float amount = i/(float)max;
|
||||
//if(max == 0)
|
||||
//amount = 0;
|
||||
|
||||
vec2 new_pos = pos + vel*fraction; // TODO: this row is not nice
|
||||
|
||||
if(test_box(vec2(new_pos.x, new_pos.y), size))
|
||||
{
|
||||
int hits = 0;
|
||||
|
||||
if(test_box(vec2(pos.x, new_pos.y), size))
|
||||
{
|
||||
new_pos.y = pos.y;
|
||||
vel.y *= -elasticity;
|
||||
hits++;
|
||||
}
|
||||
|
||||
if(test_box(vec2(new_pos.x, pos.y), size))
|
||||
{
|
||||
new_pos.x = pos.x;
|
||||
vel.x *= -elasticity;
|
||||
hits++;
|
||||
}
|
||||
|
||||
// neither of the tests got a collision.
|
||||
// this is a real _corner case_!
|
||||
if(hits == 0)
|
||||
{
|
||||
new_pos.y = pos.y;
|
||||
vel.y *= -elasticity;
|
||||
new_pos.x = pos.x;
|
||||
vel.x *= -elasticity;
|
||||
}
|
||||
}
|
||||
|
||||
pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
*inout_pos = pos;
|
||||
*inout_vel = vel;
|
||||
}
|
||||
|
||||
float hermite_basis1(float v)
|
||||
{
|
||||
return 2*v*v*v - 3*v*v+1;
|
||||
}
|
||||
|
||||
float velocity_ramp(float value, float start, float range, float curvature)
|
||||
{
|
||||
if(value < start)
|
||||
return 1.0f;
|
||||
return 1.0f/pow(curvature, (value-start)/range);
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::reset()
|
||||
{
|
||||
pos = vec2(0,0);
|
||||
vel = vec2(0,0);
|
||||
hook_pos = vec2(0,0);
|
||||
hook_dir = vec2(0,0);
|
||||
hook_tick = 0;
|
||||
hook_state = HOOK_IDLE;
|
||||
hooked_player = -1;
|
||||
jumped = 0;
|
||||
triggered_events = 0;
|
||||
}
|
||||
|
||||
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(pos.x+phys_size/2, pos.y+phys_size/2+5))
|
||||
grounded = true;
|
||||
if(col_check_point(pos.x-phys_size/2, pos.y+phys_size/2+5))
|
||||
grounded = true;
|
||||
|
||||
vec2 target_direction = normalize(vec2(input.target_x, input.target_y));
|
||||
|
||||
vel.y += world->tuning.gravity;
|
||||
|
||||
float max_speed = grounded ? world->tuning.ground_control_speed : world->tuning.air_control_speed;
|
||||
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 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(direction > 0)
|
||||
vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
|
||||
if(direction == 0)
|
||||
vel.x *= friction;
|
||||
|
||||
// handle jumping
|
||||
// 1 bit = to keep track if a jump has been made on this input
|
||||
// 2 bit = to keep track if a air-jump has been made
|
||||
if(grounded)
|
||||
jumped &= ~2;
|
||||
|
||||
// do hook
|
||||
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_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;
|
||||
bool going_to_retract = false;
|
||||
int hit = col_intersect_line(hook_pos, new_pos, &new_pos, 0);
|
||||
if(hit)
|
||||
{
|
||||
if(hit&COLFLAG_NOHOOK)
|
||||
going_to_retract = true;
|
||||
else
|
||||
going_to_hit_ground = true;
|
||||
}
|
||||
|
||||
// Check against other players first
|
||||
if(world && world->tuning.player_hooking)
|
||||
{
|
||||
float dist = 0.0f;
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
CHARACTER_CORE *p = world->characters[i];
|
||||
if(!p || p == this)
|
||||
continue;
|
||||
|
||||
vec2 closest_point = closest_point_on_line(hook_pos, new_pos, p->pos);
|
||||
if(distance(p->pos, closest_point) < phys_size+2.0f)
|
||||
{
|
||||
if (hooked_player == -1 || distance (hook_pos, p->pos) < dist)
|
||||
{
|
||||
triggered_events |= COREEVENT_HOOK_ATTACH_PLAYER;
|
||||
hook_state = HOOK_GRABBED;
|
||||
hooked_player = i;
|
||||
dist = distance (hook_pos, p->pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
// check against ground
|
||||
if(going_to_hit_ground)
|
||||
{
|
||||
triggered_events |= COREEVENT_HOOK_ATTACH_GROUND;
|
||||
hook_state = HOOK_GRABBED;
|
||||
}
|
||||
else if(going_to_retract)
|
||||
{
|
||||
triggered_events |= COREEVENT_HOOK_HIT_NOHOOK;
|
||||
hook_state = HOOK_RETRACT_START;
|
||||
}
|
||||
|
||||
hook_pos = new_pos;
|
||||
}
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_GRABBED)
|
||||
{
|
||||
if(hooked_player != -1)
|
||||
{
|
||||
CHARACTER_CORE *p = world->characters[hooked_player];
|
||||
if(p)
|
||||
hook_pos = p->pos;
|
||||
else
|
||||
{
|
||||
// release hook
|
||||
hooked_player = -1;
|
||||
hook_state = HOOK_RETRACTED;
|
||||
hook_pos = pos;
|
||||
}
|
||||
|
||||
// keep players hooked for a max of 1.5sec
|
||||
//if(server_tick() > hook_tick+(server_tickspeed()*3)/2)
|
||||
//release_hooked();
|
||||
}
|
||||
|
||||
// don't do this hook rutine when we are hook to a player
|
||||
if(hooked_player == -1 && distance(hook_pos, pos) > 46.0f)
|
||||
{
|
||||
vec2 hookvel = normalize(hook_pos-pos)*world->tuning.hook_drag_accel;
|
||||
// the hook as more power to drag you up then down.
|
||||
// this makes it easier to get on top of an platform
|
||||
if(hookvel.y > 0)
|
||||
hookvel.y *= 0.3f;
|
||||
|
||||
// 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 && direction < 0) || (hookvel.x > 0 && direction > 0))
|
||||
hookvel.x *= 0.95f;
|
||||
else
|
||||
hookvel.x *= 0.75f;
|
||||
|
||||
vec2 new_vel = vel+hookvel;
|
||||
|
||||
// check if we are under the legal limit for the hook
|
||||
if(length(new_vel) < world->tuning.hook_drag_speed || length(new_vel) < length(vel))
|
||||
vel = new_vel; // no problem. apply
|
||||
|
||||
}
|
||||
|
||||
// release hook (max hook time is 1.25
|
||||
hook_tick++;
|
||||
if(hooked_player != -1 && (hook_tick > SERVER_TICK_SPEED+SERVER_TICK_SPEED/5 || !world->characters[hooked_player]))
|
||||
{
|
||||
hooked_player = -1;
|
||||
hook_state = HOOK_RETRACTED;
|
||||
hook_pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
if(world && world->tuning.player_collision)
|
||||
{
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
CHARACTER_CORE *p = world->characters[i];
|
||||
if(!p)
|
||||
continue;
|
||||
|
||||
//player *p = (player*)ent;
|
||||
if(p == this) // || !(p->flags&FLAG_ALIVE)
|
||||
continue; // make sure that we don't nudge our self
|
||||
|
||||
// handle player <-> player collision
|
||||
float d = distance(pos, p->pos);
|
||||
vec2 dir = normalize(pos - p->pos);
|
||||
if(d < phys_size*1.25f && d > 1.0f)
|
||||
{
|
||||
float a = (phys_size*1.45f - d);
|
||||
|
||||
// make sure that we don't add excess force by checking the
|
||||
// direction against the current velocity
|
||||
vec2 veldir = normalize(vel);
|
||||
float v = 1-(dot(veldir, dir)+1)/2;
|
||||
vel = vel + dir*a*(v*0.75f);
|
||||
vel = vel * 0.85f;
|
||||
}
|
||||
|
||||
// handle hook influence
|
||||
if(hooked_player == i)
|
||||
{
|
||||
if(d > phys_size*1.50f) // TODO: fix tweakable variable
|
||||
{
|
||||
float accel = world->tuning.hook_drag_accel * (d/world->tuning.hook_length);
|
||||
float drag_speed = world->tuning.hook_drag_speed;
|
||||
|
||||
// add force to the hooked player
|
||||
p->vel.x = saturated_add(-drag_speed, drag_speed, p->vel.x, accel*dir.x*1.5f);
|
||||
p->vel.y = saturated_add(-drag_speed, drag_speed, p->vel.y, accel*dir.y*1.5f);
|
||||
|
||||
// add a little bit force to the guy who has the grip
|
||||
vel.x = saturated_add(-drag_speed, drag_speed, vel.x, -accel*dir.x*0.25f);
|
||||
vel.y = saturated_add(-drag_speed, drag_speed, vel.y, -accel*dir.y*0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clamp the velocity to something sane
|
||||
if(length(vel) > 6000)
|
||||
vel = normalize(vel) * 6000;
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::move()
|
||||
{
|
||||
float rampvalue = velocity_ramp(length(vel)*50, world->tuning.velramp_start, world->tuning.velramp_range, world->tuning.velramp_curvature);
|
||||
|
||||
vel.x = vel.x*rampvalue;
|
||||
move_box(&pos, &vel, vec2(28.0f, 28.0f), 0);
|
||||
vel.x = vel.x*(1.0f/rampvalue);
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::write(NETOBJ_CHARACTER_CORE *obj_core)
|
||||
{
|
||||
obj_core->x = round(pos.x);
|
||||
obj_core->y = round(pos.y);
|
||||
|
||||
obj_core->vx = round(vel.x*256.0f);
|
||||
obj_core->vy = round(vel.y*256.0f);
|
||||
obj_core->hook_state = hook_state;
|
||||
obj_core->hook_tick = hook_tick;
|
||||
obj_core->hook_x = round(hook_pos.x);
|
||||
obj_core->hook_y = round(hook_pos.y);
|
||||
obj_core->hook_dx = round(hook_dir.x*256.0f);
|
||||
obj_core->hook_dy = round(hook_dir.y*256.0f);
|
||||
obj_core->hooked_player = hooked_player;
|
||||
obj_core->jumped = jumped;
|
||||
obj_core->direction = direction;
|
||||
obj_core->angle = angle;
|
||||
}
|
||||
|
||||
void CHARACTER_CORE::read(const NETOBJ_CHARACTER_CORE *obj_core)
|
||||
{
|
||||
pos.x = obj_core->x;
|
||||
pos.y = obj_core->y;
|
||||
vel.x = obj_core->vx/256.0f;
|
||||
vel.y = obj_core->vy/256.0f;
|
||||
hook_state = obj_core->hook_state;
|
||||
hook_tick = obj_core->hook_tick;
|
||||
hook_pos.x = obj_core->hook_x;
|
||||
hook_pos.y = obj_core->hook_y;
|
||||
hook_dir.x = obj_core->hook_dx/256.0f;
|
||||
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()
|
||||
{
|
||||
NETOBJ_CHARACTER_CORE c;
|
||||
write(&c);
|
||||
read(&c);
|
||||
}
|
||||
|
||||
200
project/jni/application/teeworlds/src/game/gamecore.hpp
Normal file
200
project/jni/application/teeworlds/src/game/gamecore.hpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef GAME_GAME_H
|
||||
#define GAME_GAME_H
|
||||
|
||||
#include <base/system.h>
|
||||
#include <base/math.hpp>
|
||||
|
||||
#include <engine/e_common_interface.h>
|
||||
#include <math.h>
|
||||
#include "collision.hpp"
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
|
||||
struct TUNING_PARAMS
|
||||
{
|
||||
TUNING_PARAMS()
|
||||
{
|
||||
const float ticks_per_second = 50.0f;
|
||||
#define MACRO_TUNING_PARAM(name,value) name.set((int)(value*100.0f));
|
||||
#include "tuning.hpp"
|
||||
#undef MACRO_TUNING_PARAM
|
||||
}
|
||||
|
||||
static const char *names[];
|
||||
|
||||
#define MACRO_TUNING_PARAM(name,value) tune_param name;
|
||||
#include "tuning.hpp"
|
||||
#undef MACRO_TUNING_PARAM
|
||||
|
||||
static int num() { return sizeof(TUNING_PARAMS)/sizeof(int); }
|
||||
bool set(int index, float value);
|
||||
bool set(const char *name, float value);
|
||||
bool get(int index, float *value);
|
||||
bool get(const char *name, float *value);
|
||||
};
|
||||
|
||||
|
||||
inline vec2 get_direction(int angle)
|
||||
{
|
||||
float a = angle/256.0f;
|
||||
return vec2(cosf(a), sinf(a));
|
||||
}
|
||||
|
||||
inline vec2 get_dir(float a)
|
||||
{
|
||||
return vec2(cosf(a), sinf(a));
|
||||
}
|
||||
|
||||
inline float get_angle(vec2 dir)
|
||||
{
|
||||
float a = atan(dir.y/dir.x);
|
||||
if(dir.x < 0)
|
||||
a = a+pi;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline void str_to_ints(int *ints, int num, const char *str)
|
||||
{
|
||||
int index = 0;
|
||||
while(num)
|
||||
{
|
||||
char buf[4] = {0,0,0,0};
|
||||
for(int c = 0; c < 4 && str[index]; c++, index++)
|
||||
buf[c] = str[index];
|
||||
*ints = ((buf[0]+128)<<24)|((buf[1]+128)<<16)|((buf[2]+128)<<8)|(buf[3]+128);
|
||||
ints++;
|
||||
num--;
|
||||
}
|
||||
|
||||
// null terminate
|
||||
ints[-1] &= 0xffffff00;
|
||||
}
|
||||
|
||||
inline void ints_to_str(const int *ints, int num, char *str)
|
||||
{
|
||||
while(num)
|
||||
{
|
||||
str[0] = (((*ints)>>24)&0xff)-128;
|
||||
str[1] = (((*ints)>>16)&0xff)-128;
|
||||
str[2] = (((*ints)>>8)&0xff)-128;
|
||||
str[3] = ((*ints)&0xff)-128;
|
||||
str += 4;
|
||||
ints++;
|
||||
num--;
|
||||
}
|
||||
|
||||
// null terminate
|
||||
str[-1] = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline vec2 calc_pos(vec2 p, vec2 v, float curvature, float speed, float t)
|
||||
{
|
||||
vec2 n;
|
||||
t *= speed;
|
||||
n.x = p.x + v.x*t;
|
||||
n.y = p.y + v.y*t + curvature/10000*(t*t);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline T saturated_add(T min, T max, T current, T modifier)
|
||||
{
|
||||
if(modifier < 0)
|
||||
{
|
||||
if(current < min)
|
||||
return current;
|
||||
current += modifier;
|
||||
if(current < min)
|
||||
current = min;
|
||||
return current;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(current > max)
|
||||
return current;
|
||||
current += modifier;
|
||||
if(current > max)
|
||||
current = max;
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces);
|
||||
void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity);
|
||||
bool test_box(vec2 pos, vec2 size);
|
||||
float velocity_ramp(float value, float start, float range, float curvature);
|
||||
|
||||
// hooking stuff
|
||||
enum
|
||||
{
|
||||
HOOK_RETRACTED=-1,
|
||||
HOOK_IDLE=0,
|
||||
HOOK_RETRACT_START=1,
|
||||
HOOK_RETRACT_END=3,
|
||||
HOOK_FLYING,
|
||||
HOOK_GRABBED,
|
||||
|
||||
COREEVENT_GROUND_JUMP=0x01,
|
||||
COREEVENT_AIR_JUMP=0x02,
|
||||
COREEVENT_HOOK_LAUNCH=0x04,
|
||||
COREEVENT_HOOK_ATTACH_PLAYER=0x08,
|
||||
COREEVENT_HOOK_ATTACH_GROUND=0x10,
|
||||
COREEVENT_HOOK_HIT_NOHOOK=0x20,
|
||||
COREEVENT_HOOK_RETRACT=0x40,
|
||||
};
|
||||
|
||||
class WORLD_CORE
|
||||
{
|
||||
public:
|
||||
WORLD_CORE()
|
||||
{
|
||||
mem_zero(characters, sizeof(characters));
|
||||
}
|
||||
|
||||
TUNING_PARAMS tuning;
|
||||
class CHARACTER_CORE *characters[MAX_CLIENTS];
|
||||
};
|
||||
|
||||
class CHARACTER_CORE
|
||||
{
|
||||
public:
|
||||
WORLD_CORE *world;
|
||||
|
||||
vec2 pos;
|
||||
vec2 vel;
|
||||
|
||||
vec2 hook_pos;
|
||||
vec2 hook_dir;
|
||||
int hook_tick;
|
||||
int hook_state;
|
||||
int hooked_player;
|
||||
|
||||
int jumped;
|
||||
|
||||
int direction;
|
||||
int angle;
|
||||
NETOBJ_PLAYER_INPUT input;
|
||||
|
||||
int triggered_events;
|
||||
|
||||
void reset();
|
||||
void tick(bool use_input);
|
||||
void move();
|
||||
|
||||
void read(const NETOBJ_CHARACTER_CORE *obj_core);
|
||||
void write(NETOBJ_CHARACTER_CORE *obj_core);
|
||||
void quantize();
|
||||
};
|
||||
|
||||
|
||||
#define LERP(a,b,t) (a + (b-a) * t)
|
||||
#define min(a, b) ( a > b ? b : a)
|
||||
#define max(a, b) ( a > b ? a : b)
|
||||
|
||||
inline bool col_check_point(float x, float y) { return col_is_solid(round(x), round(y)) != 0; }
|
||||
inline bool col_check_point(vec2 p) { return col_check_point(p.x, p.y); }
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,528 @@
|
||||
#include <engine/e_common_interface.h>
|
||||
#include "g_protocol.hpp"
|
||||
const char *msg_failed_on = "";
|
||||
const char *obj_corrected_on = "";
|
||||
static int num_corrections = 0;
|
||||
int netobj_num_corrections() { return num_corrections; }
|
||||
const char *netobj_corrected_on() { return obj_corrected_on; }
|
||||
const char *netmsg_failed_on() { return msg_failed_on; }
|
||||
const int max_int = 0x7fffffff;
|
||||
static int netobj_clamp_int(const char *error_msg, int v, int min, int max)
|
||||
{
|
||||
if(v<min) { obj_corrected_on = error_msg; num_corrections++; return min; }
|
||||
if(v>max) { obj_corrected_on = error_msg; num_corrections++; return max; }
|
||||
return v;
|
||||
}
|
||||
static const char *netobj_names[] = {
|
||||
"invalid",
|
||||
"player_input",
|
||||
"projectile",
|
||||
"laser",
|
||||
"pickup",
|
||||
"flag",
|
||||
"game",
|
||||
"character_core",
|
||||
"character",
|
||||
"player_info",
|
||||
"client_info",
|
||||
"common",
|
||||
"explosion",
|
||||
"spawn",
|
||||
"hammerhit",
|
||||
"death",
|
||||
"soundglobal",
|
||||
"soundworld",
|
||||
"damageind",
|
||||
""
|
||||
};
|
||||
|
||||
static int netobj_sizes[] = {
|
||||
0,
|
||||
sizeof(NETOBJ_PLAYER_INPUT),
|
||||
sizeof(NETOBJ_PROJECTILE),
|
||||
sizeof(NETOBJ_LASER),
|
||||
sizeof(NETOBJ_PICKUP),
|
||||
sizeof(NETOBJ_FLAG),
|
||||
sizeof(NETOBJ_GAME),
|
||||
sizeof(NETOBJ_CHARACTER_CORE),
|
||||
sizeof(NETOBJ_CHARACTER),
|
||||
sizeof(NETOBJ_PLAYER_INFO),
|
||||
sizeof(NETOBJ_CLIENT_INFO),
|
||||
sizeof(NETEVENT_COMMON),
|
||||
sizeof(NETEVENT_EXPLOSION),
|
||||
sizeof(NETEVENT_SPAWN),
|
||||
sizeof(NETEVENT_HAMMERHIT),
|
||||
sizeof(NETEVENT_DEATH),
|
||||
sizeof(NETEVENT_SOUNDGLOBAL),
|
||||
sizeof(NETEVENT_SOUNDWORLD),
|
||||
sizeof(NETEVENT_DAMAGEIND),
|
||||
0
|
||||
};
|
||||
|
||||
static int validate_player_input(void *data, int size)
|
||||
{
|
||||
NETOBJ_PLAYER_INPUT *obj = (NETOBJ_PLAYER_INPUT *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("player_state", obj->player_state, 0, 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_projectile(void *data, int size)
|
||||
{
|
||||
NETOBJ_PROJECTILE *obj = (NETOBJ_PROJECTILE *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("type", obj->type, 0, NUM_WEAPONS-1);
|
||||
netobj_clamp_int("start_tick", obj->start_tick, 0, max_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_laser(void *data, int size)
|
||||
{
|
||||
NETOBJ_LASER *obj = (NETOBJ_LASER *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("start_tick", obj->start_tick, 0, max_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_pickup(void *data, int size)
|
||||
{
|
||||
NETOBJ_PICKUP *obj = (NETOBJ_PICKUP *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("type", obj->type, 0, max_int);
|
||||
netobj_clamp_int("subtype", obj->subtype, 0, max_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_flag(void *data, int size)
|
||||
{
|
||||
NETOBJ_FLAG *obj = (NETOBJ_FLAG *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("team", obj->team, 0, 1);
|
||||
netobj_clamp_int("carried_by", obj->carried_by, -2, MAX_CLIENTS-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_game(void *data, int size)
|
||||
{
|
||||
NETOBJ_GAME *obj = (NETOBJ_GAME *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("flags", obj->flags, 0, 256);
|
||||
netobj_clamp_int("round_start_tick", obj->round_start_tick, 0, max_int);
|
||||
netobj_clamp_int("game_over", obj->game_over, 0, 1);
|
||||
netobj_clamp_int("sudden_death", obj->sudden_death, 0, 1);
|
||||
netobj_clamp_int("paused", obj->paused, 0, 1);
|
||||
netobj_clamp_int("score_limit", obj->score_limit, 0, max_int);
|
||||
netobj_clamp_int("time_limit", obj->time_limit, 0, max_int);
|
||||
netobj_clamp_int("warmup", obj->warmup, 0, max_int);
|
||||
netobj_clamp_int("round_num", obj->round_num, 0, max_int);
|
||||
netobj_clamp_int("round_current", obj->round_current, 0, max_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_character_core(void *data, int size)
|
||||
{
|
||||
NETOBJ_CHARACTER_CORE *obj = (NETOBJ_CHARACTER_CORE *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("direction", obj->direction, -1, 1);
|
||||
netobj_clamp_int("jumped", obj->jumped, 0, 3);
|
||||
netobj_clamp_int("hooked_player", obj->hooked_player, 0, MAX_CLIENTS-1);
|
||||
netobj_clamp_int("hook_state", obj->hook_state, -1, 5);
|
||||
netobj_clamp_int("hook_tick", obj->hook_tick, 0, max_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_character(void *data, int size)
|
||||
{
|
||||
NETOBJ_CHARACTER *obj = (NETOBJ_CHARACTER *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("player_state", obj->player_state, 0, NUM_PLAYERSTATES-1);
|
||||
netobj_clamp_int("health", obj->health, 0, 10);
|
||||
netobj_clamp_int("armor", obj->armor, 0, 10);
|
||||
netobj_clamp_int("ammocount", obj->ammocount, 0, 10);
|
||||
netobj_clamp_int("weapon", obj->weapon, 0, NUM_WEAPONS-1);
|
||||
netobj_clamp_int("emote", obj->emote, 0, 6);
|
||||
netobj_clamp_int("attacktick", obj->attacktick, 0, max_int);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_player_info(void *data, int size)
|
||||
{
|
||||
NETOBJ_PLAYER_INFO *obj = (NETOBJ_PLAYER_INFO *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("local", obj->local, 0, 1);
|
||||
netobj_clamp_int("cid", obj->cid, 0, MAX_CLIENTS-1);
|
||||
netobj_clamp_int("team", obj->team, -1, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_client_info(void *data, int size)
|
||||
{
|
||||
NETOBJ_CLIENT_INFO *obj = (NETOBJ_CLIENT_INFO *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("use_custom_color", obj->use_custom_color, 0, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_common(void *data, int size)
|
||||
{
|
||||
NETEVENT_COMMON *obj = (NETEVENT_COMMON *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_explosion(void *data, int size)
|
||||
{
|
||||
NETEVENT_EXPLOSION *obj = (NETEVENT_EXPLOSION *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_spawn(void *data, int size)
|
||||
{
|
||||
NETEVENT_SPAWN *obj = (NETEVENT_SPAWN *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_hammerhit(void *data, int size)
|
||||
{
|
||||
NETEVENT_HAMMERHIT *obj = (NETEVENT_HAMMERHIT *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_death(void *data, int size)
|
||||
{
|
||||
NETEVENT_DEATH *obj = (NETEVENT_DEATH *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("cid", obj->cid, 0, MAX_CLIENTS-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_soundglobal(void *data, int size)
|
||||
{
|
||||
NETEVENT_SOUNDGLOBAL *obj = (NETEVENT_SOUNDGLOBAL *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("soundid", obj->soundid, 0, NUM_SOUNDS-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_soundworld(void *data, int size)
|
||||
{
|
||||
NETEVENT_SOUNDWORLD *obj = (NETEVENT_SOUNDWORLD *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
netobj_clamp_int("soundid", obj->soundid, 0, NUM_SOUNDS-1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int validate_damageind(void *data, int size)
|
||||
{
|
||||
NETEVENT_DAMAGEIND *obj = (NETEVENT_DAMAGEIND *)data;
|
||||
if(sizeof(*obj) != size) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_motd()
|
||||
{
|
||||
static NETMSG_SV_MOTD msg;
|
||||
msg.message = msg_unpack_string();
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_broadcast()
|
||||
{
|
||||
static NETMSG_SV_BROADCAST msg;
|
||||
msg.message = msg_unpack_string();
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_chat()
|
||||
{
|
||||
static NETMSG_SV_CHAT msg;
|
||||
msg.team = msg_unpack_int();
|
||||
msg.cid = msg_unpack_int();
|
||||
msg.message = msg_unpack_string();
|
||||
if(msg.team < -1 || msg.team > 1) { msg_failed_on = "team"; return 0; }
|
||||
if(msg.cid < -1 || msg.cid > MAX_CLIENTS-1) { msg_failed_on = "cid"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_killmsg()
|
||||
{
|
||||
static NETMSG_SV_KILLMSG msg;
|
||||
msg.killer = msg_unpack_int();
|
||||
msg.victim = msg_unpack_int();
|
||||
msg.weapon = msg_unpack_int();
|
||||
msg.mode_special = msg_unpack_int();
|
||||
if(msg.killer < 0 || msg.killer > MAX_CLIENTS-1) { msg_failed_on = "killer"; return 0; }
|
||||
if(msg.victim < 0 || msg.victim > MAX_CLIENTS-1) { msg_failed_on = "victim"; return 0; }
|
||||
if(msg.weapon < -3 || msg.weapon > NUM_WEAPONS-1) { msg_failed_on = "weapon"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_soundglobal()
|
||||
{
|
||||
static NETMSG_SV_SOUNDGLOBAL msg;
|
||||
msg.soundid = msg_unpack_int();
|
||||
if(msg.soundid < 0 || msg.soundid > NUM_SOUNDS-1) { msg_failed_on = "soundid"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_tuneparams()
|
||||
{
|
||||
static NETMSG_SV_TUNEPARAMS msg;
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_extraprojectile()
|
||||
{
|
||||
static NETMSG_SV_EXTRAPROJECTILE msg;
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_readytoenter()
|
||||
{
|
||||
static NETMSG_SV_READYTOENTER msg;
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_weaponpickup()
|
||||
{
|
||||
static NETMSG_SV_WEAPONPICKUP msg;
|
||||
msg.weapon = msg_unpack_int();
|
||||
if(msg.weapon < 0 || msg.weapon > NUM_WEAPONS-1) { msg_failed_on = "weapon"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_emoticon()
|
||||
{
|
||||
static NETMSG_SV_EMOTICON msg;
|
||||
msg.cid = msg_unpack_int();
|
||||
msg.emoticon = msg_unpack_int();
|
||||
if(msg.cid < 0 || msg.cid > MAX_CLIENTS-1) { msg_failed_on = "cid"; return 0; }
|
||||
if(msg.emoticon < 0 || msg.emoticon > NUM_EMOTICONS-1) { msg_failed_on = "emoticon"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_vote_clearoptions()
|
||||
{
|
||||
static NETMSG_SV_VOTE_CLEAROPTIONS msg;
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_vote_option()
|
||||
{
|
||||
static NETMSG_SV_VOTE_OPTION msg;
|
||||
msg.command = msg_unpack_string();
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_vote_set()
|
||||
{
|
||||
static NETMSG_SV_VOTE_SET msg;
|
||||
msg.timeout = msg_unpack_int();
|
||||
msg.description = msg_unpack_string();
|
||||
msg.command = msg_unpack_string();
|
||||
if(msg.timeout < 0 || msg.timeout > 60) { msg_failed_on = "timeout"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_sv_vote_status()
|
||||
{
|
||||
static NETMSG_SV_VOTE_STATUS msg;
|
||||
msg.yes = msg_unpack_int();
|
||||
msg.no = msg_unpack_int();
|
||||
msg.pass = msg_unpack_int();
|
||||
msg.total = msg_unpack_int();
|
||||
if(msg.yes < 0 || msg.yes > MAX_CLIENTS) { msg_failed_on = "yes"; return 0; }
|
||||
if(msg.no < 0 || msg.no > MAX_CLIENTS) { msg_failed_on = "no"; return 0; }
|
||||
if(msg.pass < 0 || msg.pass > MAX_CLIENTS) { msg_failed_on = "pass"; return 0; }
|
||||
if(msg.total < 0 || msg.total > MAX_CLIENTS) { msg_failed_on = "total"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_say()
|
||||
{
|
||||
static NETMSG_CL_SAY msg;
|
||||
msg.team = msg_unpack_int();
|
||||
msg.message = msg_unpack_string();
|
||||
if(msg.team < 0 || msg.team > 1) { msg_failed_on = "team"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_setteam()
|
||||
{
|
||||
static NETMSG_CL_SETTEAM msg;
|
||||
msg.team = msg_unpack_int();
|
||||
if(msg.team < -1 || msg.team > 1) { msg_failed_on = "team"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_startinfo()
|
||||
{
|
||||
static NETMSG_CL_STARTINFO msg;
|
||||
msg.name = msg_unpack_string();
|
||||
msg.skin = msg_unpack_string();
|
||||
msg.use_custom_color = msg_unpack_int();
|
||||
msg.color_body = msg_unpack_int();
|
||||
msg.color_feet = msg_unpack_int();
|
||||
if(msg.use_custom_color < 0 || msg.use_custom_color > 1) { msg_failed_on = "use_custom_color"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_changeinfo()
|
||||
{
|
||||
static NETMSG_CL_CHANGEINFO msg;
|
||||
msg.name = msg_unpack_string();
|
||||
msg.skin = msg_unpack_string();
|
||||
msg.use_custom_color = msg_unpack_int();
|
||||
msg.color_body = msg_unpack_int();
|
||||
msg.color_feet = msg_unpack_int();
|
||||
if(msg.use_custom_color < 0 || msg.use_custom_color > 1) { msg_failed_on = "use_custom_color"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_kill()
|
||||
{
|
||||
static NETMSG_CL_KILL msg;
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_emoticon()
|
||||
{
|
||||
static NETMSG_CL_EMOTICON msg;
|
||||
msg.emoticon = msg_unpack_int();
|
||||
if(msg.emoticon < 0 || msg.emoticon > NUM_EMOTICONS-1) { msg_failed_on = "emoticon"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_vote()
|
||||
{
|
||||
static NETMSG_CL_VOTE msg;
|
||||
msg.vote = msg_unpack_int();
|
||||
if(msg.vote < -1 || msg.vote > 1) { msg_failed_on = "vote"; return 0; }
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static void *secure_unpack_cl_callvote()
|
||||
{
|
||||
static NETMSG_CL_CALLVOTE msg;
|
||||
msg.type = msg_unpack_string();
|
||||
msg.value = msg_unpack_string();
|
||||
return &msg;
|
||||
}
|
||||
|
||||
static int validate_invalid(void *data, int size) { return -1; }
|
||||
typedef int(*VALIDATEFUNC)(void *data, int size);
|
||||
static VALIDATEFUNC validate_funcs[] = {
|
||||
validate_invalid,
|
||||
validate_player_input,
|
||||
validate_projectile,
|
||||
validate_laser,
|
||||
validate_pickup,
|
||||
validate_flag,
|
||||
validate_game,
|
||||
validate_character_core,
|
||||
validate_character,
|
||||
validate_player_info,
|
||||
validate_client_info,
|
||||
validate_common,
|
||||
validate_explosion,
|
||||
validate_spawn,
|
||||
validate_hammerhit,
|
||||
validate_death,
|
||||
validate_soundglobal,
|
||||
validate_soundworld,
|
||||
validate_damageind,
|
||||
0x0
|
||||
};
|
||||
|
||||
int netobj_validate(int type, void *data, int size)
|
||||
{
|
||||
if(type < 0 || type >= NUM_NETOBJTYPES) return -1;
|
||||
return validate_funcs[type](data, size);
|
||||
};
|
||||
|
||||
const char *netobj_get_name(int type)
|
||||
{
|
||||
if(type < 0 || type >= NUM_NETOBJTYPES) return "(out of range)";
|
||||
return netobj_names[type];
|
||||
};
|
||||
|
||||
int netobj_get_size(int type)
|
||||
{
|
||||
if(type < 0 || type >= NUM_NETOBJTYPES) return 0;
|
||||
return netobj_sizes[type];
|
||||
};
|
||||
|
||||
static void *secure_unpack_invalid() { return 0; }
|
||||
typedef void *(*SECUREUNPACKFUNC)();
|
||||
static SECUREUNPACKFUNC secure_unpack_funcs[] = {
|
||||
secure_unpack_invalid,
|
||||
secure_unpack_sv_motd,
|
||||
secure_unpack_sv_broadcast,
|
||||
secure_unpack_sv_chat,
|
||||
secure_unpack_sv_killmsg,
|
||||
secure_unpack_sv_soundglobal,
|
||||
secure_unpack_sv_tuneparams,
|
||||
secure_unpack_sv_extraprojectile,
|
||||
secure_unpack_sv_readytoenter,
|
||||
secure_unpack_sv_weaponpickup,
|
||||
secure_unpack_sv_emoticon,
|
||||
secure_unpack_sv_vote_clearoptions,
|
||||
secure_unpack_sv_vote_option,
|
||||
secure_unpack_sv_vote_set,
|
||||
secure_unpack_sv_vote_status,
|
||||
secure_unpack_cl_say,
|
||||
secure_unpack_cl_setteam,
|
||||
secure_unpack_cl_startinfo,
|
||||
secure_unpack_cl_changeinfo,
|
||||
secure_unpack_cl_kill,
|
||||
secure_unpack_cl_emoticon,
|
||||
secure_unpack_cl_vote,
|
||||
secure_unpack_cl_callvote,
|
||||
0x0
|
||||
};
|
||||
void *netmsg_secure_unpack(int type)
|
||||
{
|
||||
void *msg;
|
||||
msg_failed_on = "";
|
||||
if(type < 0 || type >= NUM_NETMSGTYPES) { msg_failed_on = "(type out of range)"; return 0; }
|
||||
msg = secure_unpack_funcs[type]();
|
||||
if(msg_unpack_error()) { msg_failed_on = "(unpack error)"; return 0; }
|
||||
return msg;
|
||||
};
|
||||
|
||||
static const char *message_names[] = {
|
||||
"invalid",
|
||||
"sv_motd",
|
||||
"sv_broadcast",
|
||||
"sv_chat",
|
||||
"sv_killmsg",
|
||||
"sv_soundglobal",
|
||||
"sv_tuneparams",
|
||||
"sv_extraprojectile",
|
||||
"sv_readytoenter",
|
||||
"sv_weaponpickup",
|
||||
"sv_emoticon",
|
||||
"sv_vote_clearoptions",
|
||||
"sv_vote_option",
|
||||
"sv_vote_set",
|
||||
"sv_vote_status",
|
||||
"cl_say",
|
||||
"cl_setteam",
|
||||
"cl_startinfo",
|
||||
"cl_changeinfo",
|
||||
"cl_kill",
|
||||
"cl_emoticon",
|
||||
"cl_vote",
|
||||
"cl_callvote",
|
||||
""
|
||||
};
|
||||
|
||||
const char *netmsg_get_name(int type)
|
||||
{
|
||||
if(type < 0 || type >= NUM_NETMSGTYPES) return "(out of range)";
|
||||
return message_names[type];
|
||||
};
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
#ifndef FILE_G_PROTOCOL_H
|
||||
#define FILE_G_PROTOCOL_H
|
||||
|
||||
enum
|
||||
{
|
||||
INPUT_STATE_MASK=0x3f,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PLAYERSTATE_UNKNOWN=0,
|
||||
PLAYERSTATE_PLAYING,
|
||||
PLAYERSTATE_IN_MENU,
|
||||
PLAYERSTATE_CHATTING,
|
||||
NUM_PLAYERSTATES
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
EMOTE_NORMAL=0,
|
||||
EMOTE_PAIN,
|
||||
EMOTE_HAPPY,
|
||||
EMOTE_SURPRISE,
|
||||
EMOTE_ANGRY,
|
||||
EMOTE_BLINK,
|
||||
NUM_EMOTES
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
POWERUP_HEALTH=0,
|
||||
POWERUP_ARMOR,
|
||||
POWERUP_WEAPON,
|
||||
POWERUP_NINJA,
|
||||
NUM_POWERUPS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
EMOTICON_1=0,
|
||||
EMOTICON_2,
|
||||
EMOTICON_3,
|
||||
EMOTICON_4,
|
||||
EMOTICON_5,
|
||||
EMOTICON_6,
|
||||
EMOTICON_7,
|
||||
EMOTICON_8,
|
||||
EMOTICON_9,
|
||||
EMOTICON_10,
|
||||
EMOTICON_11,
|
||||
EMOTICON_12,
|
||||
EMOTICON_13,
|
||||
EMOTICON_14,
|
||||
EMOTICON_15,
|
||||
NUM_EMOTICONS
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
GAMEFLAG_TEAMS = 1<<0,
|
||||
GAMEFLAG_FLAGS = 1<<1,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NETOBJ_INVALID=0,
|
||||
NETOBJTYPE_PLAYER_INPUT,
|
||||
NETOBJTYPE_PROJECTILE,
|
||||
NETOBJTYPE_LASER,
|
||||
NETOBJTYPE_PICKUP,
|
||||
NETOBJTYPE_FLAG,
|
||||
NETOBJTYPE_GAME,
|
||||
NETOBJTYPE_CHARACTER_CORE,
|
||||
NETOBJTYPE_CHARACTER,
|
||||
NETOBJTYPE_PLAYER_INFO,
|
||||
NETOBJTYPE_CLIENT_INFO,
|
||||
NETEVENTTYPE_COMMON,
|
||||
NETEVENTTYPE_EXPLOSION,
|
||||
NETEVENTTYPE_SPAWN,
|
||||
NETEVENTTYPE_HAMMERHIT,
|
||||
NETEVENTTYPE_DEATH,
|
||||
NETEVENTTYPE_SOUNDGLOBAL,
|
||||
NETEVENTTYPE_SOUNDWORLD,
|
||||
NETEVENTTYPE_DAMAGEIND,
|
||||
NUM_NETOBJTYPES
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
NETMSG_INVALID=0,
|
||||
NETMSGTYPE_SV_MOTD,
|
||||
NETMSGTYPE_SV_BROADCAST,
|
||||
NETMSGTYPE_SV_CHAT,
|
||||
NETMSGTYPE_SV_KILLMSG,
|
||||
NETMSGTYPE_SV_SOUNDGLOBAL,
|
||||
NETMSGTYPE_SV_TUNEPARAMS,
|
||||
NETMSGTYPE_SV_EXTRAPROJECTILE,
|
||||
NETMSGTYPE_SV_READYTOENTER,
|
||||
NETMSGTYPE_SV_WEAPONPICKUP,
|
||||
NETMSGTYPE_SV_EMOTICON,
|
||||
NETMSGTYPE_SV_VOTE_CLEAROPTIONS,
|
||||
NETMSGTYPE_SV_VOTE_OPTION,
|
||||
NETMSGTYPE_SV_VOTE_SET,
|
||||
NETMSGTYPE_SV_VOTE_STATUS,
|
||||
NETMSGTYPE_CL_SAY,
|
||||
NETMSGTYPE_CL_SETTEAM,
|
||||
NETMSGTYPE_CL_STARTINFO,
|
||||
NETMSGTYPE_CL_CHANGEINFO,
|
||||
NETMSGTYPE_CL_KILL,
|
||||
NETMSGTYPE_CL_EMOTICON,
|
||||
NETMSGTYPE_CL_VOTE,
|
||||
NETMSGTYPE_CL_CALLVOTE,
|
||||
NUM_NETMSGTYPES
|
||||
};
|
||||
|
||||
struct NETOBJ_PLAYER_INPUT
|
||||
{
|
||||
int direction;
|
||||
int target_x;
|
||||
int target_y;
|
||||
int jump;
|
||||
int fire;
|
||||
int hook;
|
||||
int player_state;
|
||||
int wanted_weapon;
|
||||
int next_weapon;
|
||||
int prev_weapon;
|
||||
};
|
||||
|
||||
struct NETOBJ_PROJECTILE
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int vx;
|
||||
int vy;
|
||||
int type;
|
||||
int start_tick;
|
||||
};
|
||||
|
||||
struct NETOBJ_LASER
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int from_x;
|
||||
int from_y;
|
||||
int start_tick;
|
||||
};
|
||||
|
||||
struct NETOBJ_PICKUP
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int type;
|
||||
int subtype;
|
||||
};
|
||||
|
||||
struct NETOBJ_FLAG
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int team;
|
||||
int carried_by;
|
||||
};
|
||||
|
||||
struct NETOBJ_GAME
|
||||
{
|
||||
int flags;
|
||||
int round_start_tick;
|
||||
int game_over;
|
||||
int sudden_death;
|
||||
int paused;
|
||||
int score_limit;
|
||||
int time_limit;
|
||||
int warmup;
|
||||
int round_num;
|
||||
int round_current;
|
||||
int teamscore_red;
|
||||
int teamscore_blue;
|
||||
};
|
||||
|
||||
struct NETOBJ_CHARACTER_CORE
|
||||
{
|
||||
int tick;
|
||||
int x;
|
||||
int y;
|
||||
int vx;
|
||||
int vy;
|
||||
int angle;
|
||||
int direction;
|
||||
int jumped;
|
||||
int hooked_player;
|
||||
int hook_state;
|
||||
int hook_tick;
|
||||
int hook_x;
|
||||
int hook_y;
|
||||
int hook_dx;
|
||||
int hook_dy;
|
||||
};
|
||||
|
||||
struct NETOBJ_CHARACTER : public NETOBJ_CHARACTER_CORE
|
||||
{
|
||||
int player_state;
|
||||
int health;
|
||||
int armor;
|
||||
int ammocount;
|
||||
int weapon;
|
||||
int emote;
|
||||
int attacktick;
|
||||
};
|
||||
|
||||
struct NETOBJ_PLAYER_INFO
|
||||
{
|
||||
int local;
|
||||
int cid;
|
||||
int team;
|
||||
int score;
|
||||
int latency;
|
||||
int latency_flux;
|
||||
};
|
||||
|
||||
struct NETOBJ_CLIENT_INFO
|
||||
{
|
||||
int name0;
|
||||
int name1;
|
||||
int name2;
|
||||
int name3;
|
||||
int name4;
|
||||
int name5;
|
||||
int skin0;
|
||||
int skin1;
|
||||
int skin2;
|
||||
int skin3;
|
||||
int skin4;
|
||||
int skin5;
|
||||
int use_custom_color;
|
||||
int color_body;
|
||||
int color_feet;
|
||||
};
|
||||
|
||||
struct NETEVENT_COMMON
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
struct NETEVENT_EXPLOSION : public NETEVENT_COMMON
|
||||
{
|
||||
};
|
||||
|
||||
struct NETEVENT_SPAWN : public NETEVENT_COMMON
|
||||
{
|
||||
};
|
||||
|
||||
struct NETEVENT_HAMMERHIT : public NETEVENT_COMMON
|
||||
{
|
||||
};
|
||||
|
||||
struct NETEVENT_DEATH : public NETEVENT_COMMON
|
||||
{
|
||||
int cid;
|
||||
};
|
||||
|
||||
struct NETEVENT_SOUNDGLOBAL : public NETEVENT_COMMON
|
||||
{
|
||||
int soundid;
|
||||
};
|
||||
|
||||
struct NETEVENT_SOUNDWORLD : public NETEVENT_COMMON
|
||||
{
|
||||
int soundid;
|
||||
};
|
||||
|
||||
struct NETEVENT_DAMAGEIND : public NETEVENT_COMMON
|
||||
{
|
||||
int angle;
|
||||
};
|
||||
|
||||
struct NETMSG_SV_MOTD
|
||||
{
|
||||
const char *message;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_MOTD, flags);
|
||||
msg_pack_string(message, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_BROADCAST
|
||||
{
|
||||
const char *message;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_BROADCAST, flags);
|
||||
msg_pack_string(message, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_CHAT
|
||||
{
|
||||
int team;
|
||||
int cid;
|
||||
const char *message;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_CHAT, flags);
|
||||
msg_pack_int(team);
|
||||
msg_pack_int(cid);
|
||||
msg_pack_string(message, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_KILLMSG
|
||||
{
|
||||
int killer;
|
||||
int victim;
|
||||
int weapon;
|
||||
int mode_special;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_KILLMSG, flags);
|
||||
msg_pack_int(killer);
|
||||
msg_pack_int(victim);
|
||||
msg_pack_int(weapon);
|
||||
msg_pack_int(mode_special);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_SOUNDGLOBAL
|
||||
{
|
||||
int soundid;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_SOUNDGLOBAL, flags);
|
||||
msg_pack_int(soundid);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_TUNEPARAMS
|
||||
{
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_TUNEPARAMS, flags);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_EXTRAPROJECTILE
|
||||
{
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, flags);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_READYTOENTER
|
||||
{
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_READYTOENTER, flags);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_WEAPONPICKUP
|
||||
{
|
||||
int weapon;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_WEAPONPICKUP, flags);
|
||||
msg_pack_int(weapon);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_EMOTICON
|
||||
{
|
||||
int cid;
|
||||
int emoticon;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_EMOTICON, flags);
|
||||
msg_pack_int(cid);
|
||||
msg_pack_int(emoticon);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_VOTE_CLEAROPTIONS
|
||||
{
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_VOTE_CLEAROPTIONS, flags);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_VOTE_OPTION
|
||||
{
|
||||
const char *command;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_VOTE_OPTION, flags);
|
||||
msg_pack_string(command, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_VOTE_SET
|
||||
{
|
||||
int timeout;
|
||||
const char *description;
|
||||
const char *command;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_VOTE_SET, flags);
|
||||
msg_pack_int(timeout);
|
||||
msg_pack_string(description, -1);
|
||||
msg_pack_string(command, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_SV_VOTE_STATUS
|
||||
{
|
||||
int yes;
|
||||
int no;
|
||||
int pass;
|
||||
int total;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_SV_VOTE_STATUS, flags);
|
||||
msg_pack_int(yes);
|
||||
msg_pack_int(no);
|
||||
msg_pack_int(pass);
|
||||
msg_pack_int(total);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_SAY
|
||||
{
|
||||
int team;
|
||||
const char *message;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_SAY, flags);
|
||||
msg_pack_int(team);
|
||||
msg_pack_string(message, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_SETTEAM
|
||||
{
|
||||
int team;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_SETTEAM, flags);
|
||||
msg_pack_int(team);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_STARTINFO
|
||||
{
|
||||
const char *name;
|
||||
const char *skin;
|
||||
int use_custom_color;
|
||||
int color_body;
|
||||
int color_feet;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_STARTINFO, flags);
|
||||
msg_pack_string(name, -1);
|
||||
msg_pack_string(skin, -1);
|
||||
msg_pack_int(use_custom_color);
|
||||
msg_pack_int(color_body);
|
||||
msg_pack_int(color_feet);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_CHANGEINFO
|
||||
{
|
||||
const char *name;
|
||||
const char *skin;
|
||||
int use_custom_color;
|
||||
int color_body;
|
||||
int color_feet;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_CHANGEINFO, flags);
|
||||
msg_pack_string(name, -1);
|
||||
msg_pack_string(skin, -1);
|
||||
msg_pack_int(use_custom_color);
|
||||
msg_pack_int(color_body);
|
||||
msg_pack_int(color_feet);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_KILL
|
||||
{
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_KILL, flags);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_EMOTICON
|
||||
{
|
||||
int emoticon;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_EMOTICON, flags);
|
||||
msg_pack_int(emoticon);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_VOTE
|
||||
{
|
||||
int vote;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_VOTE, flags);
|
||||
msg_pack_int(vote);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
struct NETMSG_CL_CALLVOTE
|
||||
{
|
||||
const char *type;
|
||||
const char *value;
|
||||
void pack(int flags)
|
||||
{
|
||||
msg_pack_start(NETMSGTYPE_CL_CALLVOTE, flags);
|
||||
msg_pack_string(type, -1);
|
||||
msg_pack_string(value, -1);
|
||||
msg_pack_end();
|
||||
}
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SOUND_GUN_FIRE=0,
|
||||
SOUND_SHOTGUN_FIRE,
|
||||
SOUND_GRENADE_FIRE,
|
||||
SOUND_HAMMER_FIRE,
|
||||
SOUND_HAMMER_HIT,
|
||||
SOUND_NINJA_FIRE,
|
||||
SOUND_GRENADE_EXPLODE,
|
||||
SOUND_NINJA_HIT,
|
||||
SOUND_RIFLE_FIRE,
|
||||
SOUND_RIFLE_BOUNCE,
|
||||
SOUND_WEAPON_SWITCH,
|
||||
SOUND_PLAYER_PAIN_SHORT,
|
||||
SOUND_PLAYER_PAIN_LONG,
|
||||
SOUND_BODY_LAND,
|
||||
SOUND_PLAYER_AIRJUMP,
|
||||
SOUND_PLAYER_JUMP,
|
||||
SOUND_PLAYER_DIE,
|
||||
SOUND_PLAYER_SPAWN,
|
||||
SOUND_PLAYER_SKID,
|
||||
SOUND_TEE_CRY,
|
||||
SOUND_HOOK_LOOP,
|
||||
SOUND_HOOK_ATTACH_GROUND,
|
||||
SOUND_HOOK_ATTACH_PLAYER,
|
||||
SOUND_HOOK_NOATTACH,
|
||||
SOUND_PICKUP_HEALTH,
|
||||
SOUND_PICKUP_ARMOR,
|
||||
SOUND_PICKUP_GRENADE,
|
||||
SOUND_PICKUP_SHOTGUN,
|
||||
SOUND_PICKUP_NINJA,
|
||||
SOUND_WEAPON_SPAWN,
|
||||
SOUND_WEAPON_NOAMMO,
|
||||
SOUND_HIT,
|
||||
SOUND_CHAT_SERVER,
|
||||
SOUND_CHAT_CLIENT,
|
||||
SOUND_CTF_DROP,
|
||||
SOUND_CTF_RETURN,
|
||||
SOUND_CTF_GRAB_PL,
|
||||
SOUND_CTF_GRAB_EN,
|
||||
SOUND_CTF_CAPTURE,
|
||||
NUM_SOUNDS
|
||||
};
|
||||
enum
|
||||
{
|
||||
WEAPON_HAMMER=0,
|
||||
WEAPON_GUN,
|
||||
WEAPON_SHOTGUN,
|
||||
WEAPON_GRENADE,
|
||||
WEAPON_RIFLE,
|
||||
WEAPON_NINJA,
|
||||
NUM_WEAPONS
|
||||
};
|
||||
int netobj_validate(int type, void *data, int size);
|
||||
const char *netobj_get_name(int type);
|
||||
int netobj_get_size(int type);
|
||||
void *netmsg_secure_unpack(int type);
|
||||
const char *netmsg_get_name(int type);
|
||||
const char *netmsg_failed_on();
|
||||
int netobj_num_corrections();
|
||||
const char *netobj_corrected_on();
|
||||
#endif // FILE_G_PROTOCOL_H
|
||||
469
project/jni/application/teeworlds/src/game/generated/gc_data.cpp
Normal file
469
project/jni/application/teeworlds/src/game/generated/gc_data.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
#include "gc_data.hpp"
|
||||
static SOUND x1890[] = {
|
||||
/* x1890[0] */ { 0, "audio/wp_gun_fire-01.wv", },
|
||||
/* x1890[1] */ { 0, "audio/wp_gun_fire-02.wv", },
|
||||
/* x1890[2] */ { 0, "audio/wp_gun_fire-03.wv", },
|
||||
};
|
||||
static SOUND x1906[] = {
|
||||
/* x1906[0] */ { 0, "audio/wp_shotty_fire-01.wv", },
|
||||
/* x1906[1] */ { 0, "audio/wp_shotty_fire-02.wv", },
|
||||
/* x1906[2] */ { 0, "audio/wp_shotty_fire-03.wv", },
|
||||
};
|
||||
static SOUND x1922[] = {
|
||||
/* x1922[0] */ { 0, "audio/wp_flump_launch-01.wv", },
|
||||
/* x1922[1] */ { 0, "audio/wp_flump_launch-02.wv", },
|
||||
/* x1922[2] */ { 0, "audio/wp_flump_launch-03.wv", },
|
||||
};
|
||||
static SOUND x1938[] = {
|
||||
/* x1938[0] */ { 0, "audio/wp_hammer_swing-01.wv", },
|
||||
/* x1938[1] */ { 0, "audio/wp_hammer_swing-02.wv", },
|
||||
/* x1938[2] */ { 0, "audio/wp_hammer_swing-03.wv", },
|
||||
};
|
||||
static SOUND x1954[] = {
|
||||
/* x1954[0] */ { 0, "audio/wp_hammer_hit-01.wv", },
|
||||
/* x1954[1] */ { 0, "audio/wp_hammer_hit-02.wv", },
|
||||
/* x1954[2] */ { 0, "audio/wp_hammer_hit-03.wv", },
|
||||
};
|
||||
static SOUND x1970[] = {
|
||||
/* x1970[0] */ { 0, "audio/wp_ninja_attack-01.wv", },
|
||||
/* x1970[1] */ { 0, "audio/wp_ninja_attack-02.wv", },
|
||||
/* x1970[2] */ { 0, "audio/wp_ninja_attack-03.wv", },
|
||||
};
|
||||
static SOUND x1986[] = {
|
||||
/* x1986[0] */ { 0, "audio/wp_flump_explo-01.wv", },
|
||||
/* x1986[1] */ { 0, "audio/wp_flump_explo-02.wv", },
|
||||
/* x1986[2] */ { 0, "audio/wp_flump_explo-03.wv", },
|
||||
};
|
||||
static SOUND x2002[] = {
|
||||
/* x2002[0] */ { 0, "audio/wp_ninja_hit-01.wv", },
|
||||
/* x2002[1] */ { 0, "audio/wp_ninja_hit-02.wv", },
|
||||
/* x2002[2] */ { 0, "audio/wp_ninja_hit-03.wv", },
|
||||
};
|
||||
static SOUND x2018[] = {
|
||||
/* x2018[0] */ { 0, "audio/wp_rifle_fire-01.wv", },
|
||||
/* x2018[1] */ { 0, "audio/wp_rifle_fire-02.wv", },
|
||||
/* x2018[2] */ { 0, "audio/wp_rifle_fire-03.wv", },
|
||||
};
|
||||
static SOUND x2034[] = {
|
||||
/* x2034[0] */ { 0, "audio/wp_rifle_bnce-01.wv", },
|
||||
/* x2034[1] */ { 0, "audio/wp_rifle_bnce-02.wv", },
|
||||
/* x2034[2] */ { 0, "audio/wp_rifle_bnce-03.wv", },
|
||||
};
|
||||
static SOUND x2050[] = {
|
||||
/* x2050[0] */ { 0, "audio/wp_switch-01.wv", },
|
||||
/* x2050[1] */ { 0, "audio/wp_switch-02.wv", },
|
||||
/* x2050[2] */ { 0, "audio/wp_switch-03.wv", },
|
||||
};
|
||||
static SOUND x2066[] = {
|
||||
/* x2066[0] */ { 0, "audio/vo_teefault_pain_short-01.wv", },
|
||||
/* x2066[1] */ { 0, "audio/vo_teefault_pain_short-02.wv", },
|
||||
/* x2066[2] */ { 0, "audio/vo_teefault_pain_short-03.wv", },
|
||||
/* x2066[3] */ { 0, "audio/vo_teefault_pain_short-04.wv", },
|
||||
/* x2066[4] */ { 0, "audio/vo_teefault_pain_short-05.wv", },
|
||||
/* x2066[5] */ { 0, "audio/vo_teefault_pain_short-06.wv", },
|
||||
/* x2066[6] */ { 0, "audio/vo_teefault_pain_short-07.wv", },
|
||||
/* x2066[7] */ { 0, "audio/vo_teefault_pain_short-08.wv", },
|
||||
/* x2066[8] */ { 0, "audio/vo_teefault_pain_short-09.wv", },
|
||||
/* x2066[9] */ { 0, "audio/vo_teefault_pain_short-10.wv", },
|
||||
/* x2066[10] */ { 0, "audio/vo_teefault_pain_short-11.wv", },
|
||||
/* x2066[11] */ { 0, "audio/vo_teefault_pain_short-12.wv", },
|
||||
};
|
||||
static SOUND x2109[] = {
|
||||
/* x2109[0] */ { 0, "audio/vo_teefault_pain_long-01.wv", },
|
||||
/* x2109[1] */ { 0, "audio/vo_teefault_pain_long-02.wv", },
|
||||
};
|
||||
static SOUND x2122[] = {
|
||||
/* x2122[0] */ { 0, "audio/foley_land-01.wv", },
|
||||
/* x2122[1] */ { 0, "audio/foley_land-02.wv", },
|
||||
/* x2122[2] */ { 0, "audio/foley_land-03.wv", },
|
||||
/* x2122[3] */ { 0, "audio/foley_land-04.wv", },
|
||||
};
|
||||
static SOUND x2141[] = {
|
||||
/* x2141[0] */ { 0, "audio/foley_dbljump-01.wv", },
|
||||
/* x2141[1] */ { 0, "audio/foley_dbljump-02.wv", },
|
||||
/* x2141[2] */ { 0, "audio/foley_dbljump-03.wv", },
|
||||
};
|
||||
static SOUND x2157[] = {
|
||||
/* x2157[0] */ { 0, "audio/foley_foot_left-01.wv", },
|
||||
/* x2157[1] */ { 0, "audio/foley_foot_left-02.wv", },
|
||||
/* x2157[2] */ { 0, "audio/foley_foot_left-03.wv", },
|
||||
/* x2157[3] */ { 0, "audio/foley_foot_left-04.wv", },
|
||||
/* x2157[4] */ { 0, "audio/foley_foot_right-01.wv", },
|
||||
/* x2157[5] */ { 0, "audio/foley_foot_right-02.wv", },
|
||||
/* x2157[6] */ { 0, "audio/foley_foot_right-03.wv", },
|
||||
/* x2157[7] */ { 0, "audio/foley_foot_right-04.wv", },
|
||||
};
|
||||
static SOUND x2188[] = {
|
||||
/* x2188[0] */ { 0, "audio/foley_body_splat-01.wv", },
|
||||
/* x2188[1] */ { 0, "audio/foley_body_splat-02.wv", },
|
||||
/* x2188[2] */ { 0, "audio/foley_body_splat-03.wv", },
|
||||
};
|
||||
static SOUND x2204[] = {
|
||||
/* x2204[0] */ { 0, "audio/vo_teefault_spawn-01.wv", },
|
||||
/* x2204[1] */ { 0, "audio/vo_teefault_spawn-02.wv", },
|
||||
/* x2204[2] */ { 0, "audio/vo_teefault_spawn-03.wv", },
|
||||
/* x2204[3] */ { 0, "audio/vo_teefault_spawn-04.wv", },
|
||||
/* x2204[4] */ { 0, "audio/vo_teefault_spawn-05.wv", },
|
||||
/* x2204[5] */ { 0, "audio/vo_teefault_spawn-06.wv", },
|
||||
/* x2204[6] */ { 0, "audio/vo_teefault_spawn-07.wv", },
|
||||
};
|
||||
static SOUND x2232[] = {
|
||||
/* x2232[0] */ { 0, "audio/sfx_skid-01.wv", },
|
||||
/* x2232[1] */ { 0, "audio/sfx_skid-02.wv", },
|
||||
/* x2232[2] */ { 0, "audio/sfx_skid-03.wv", },
|
||||
/* x2232[3] */ { 0, "audio/sfx_skid-04.wv", },
|
||||
};
|
||||
static SOUND x2251[] = {
|
||||
/* x2251[0] */ { 0, "audio/vo_teefault_cry-01.wv", },
|
||||
/* x2251[1] */ { 0, "audio/vo_teefault_cry-02.wv", },
|
||||
};
|
||||
static SOUND x2264[] = {
|
||||
/* x2264[0] */ { 0, "audio/hook_loop-01.wv", },
|
||||
/* x2264[1] */ { 0, "audio/hook_loop-02.wv", },
|
||||
};
|
||||
static SOUND x2277[] = {
|
||||
/* x2277[0] */ { 0, "audio/hook_attach-01.wv", },
|
||||
/* x2277[1] */ { 0, "audio/hook_attach-02.wv", },
|
||||
/* x2277[2] */ { 0, "audio/hook_attach-03.wv", },
|
||||
};
|
||||
static SOUND x2293[] = {
|
||||
/* x2293[0] */ { 0, "audio/foley_body_impact-01.wv", },
|
||||
/* x2293[1] */ { 0, "audio/foley_body_impact-02.wv", },
|
||||
/* x2293[2] */ { 0, "audio/foley_body_impact-03.wv", },
|
||||
};
|
||||
static SOUND x2309[] = {
|
||||
/* x2309[0] */ { 0, "audio/hook_noattach-01.wv", },
|
||||
/* x2309[1] */ { 0, "audio/hook_noattach-02.wv", },
|
||||
};
|
||||
static SOUND x2322[] = {
|
||||
/* x2322[0] */ { 0, "audio/sfx_pickup_hrt-01.wv", },
|
||||
/* x2322[1] */ { 0, "audio/sfx_pickup_hrt-02.wv", },
|
||||
};
|
||||
static SOUND x2335[] = {
|
||||
/* x2335[0] */ { 0, "audio/sfx_pickup_arm-01.wv", },
|
||||
/* x2335[1] */ { 0, "audio/sfx_pickup_arm-02.wv", },
|
||||
/* x2335[2] */ { 0, "audio/sfx_pickup_arm-03.wv", },
|
||||
/* x2335[3] */ { 0, "audio/sfx_pickup_arm-04.wv", },
|
||||
};
|
||||
static SOUND x2354[] = {
|
||||
/* x2354[0] */ { 0, "audio/sfx_pickup_launcher.wv", },
|
||||
};
|
||||
static SOUND x2364[] = {
|
||||
/* x2364[0] */ { 0, "audio/sfx_pickup_sg.wv", },
|
||||
};
|
||||
static SOUND x2374[] = {
|
||||
/* x2374[0] */ { 0, "audio/sfx_pickup_ninja.wv", },
|
||||
};
|
||||
static SOUND x2384[] = {
|
||||
/* x2384[0] */ { 0, "audio/sfx_spawn_wpn-01.wv", },
|
||||
/* x2384[1] */ { 0, "audio/sfx_spawn_wpn-02.wv", },
|
||||
/* x2384[2] */ { 0, "audio/sfx_spawn_wpn-03.wv", },
|
||||
};
|
||||
static SOUND x2400[] = {
|
||||
/* x2400[0] */ { 0, "audio/wp_noammo-01.wv", },
|
||||
/* x2400[1] */ { 0, "audio/wp_noammo-02.wv", },
|
||||
/* x2400[2] */ { 0, "audio/wp_noammo-03.wv", },
|
||||
/* x2400[3] */ { 0, "audio/wp_noammo-04.wv", },
|
||||
/* x2400[4] */ { 0, "audio/wp_noammo-05.wv", },
|
||||
};
|
||||
static SOUND x2422[] = {
|
||||
/* x2422[0] */ { 0, "audio/sfx_hit_weak-01.wv", },
|
||||
/* x2422[1] */ { 0, "audio/sfx_hit_weak-02.wv", },
|
||||
};
|
||||
static SOUND x2435[] = {
|
||||
/* x2435[0] */ { 0, "audio/sfx_msg-server.wv", },
|
||||
};
|
||||
static SOUND x2445[] = {
|
||||
/* x2445[0] */ { 0, "audio/sfx_msg-client.wv", },
|
||||
};
|
||||
static SOUND x2455[] = {
|
||||
/* x2455[0] */ { 0, "audio/sfx_ctf_drop.wv", },
|
||||
};
|
||||
static SOUND x2465[] = {
|
||||
/* x2465[0] */ { 0, "audio/sfx_ctf_rtn.wv", },
|
||||
};
|
||||
static SOUND x2475[] = {
|
||||
/* x2475[0] */ { 0, "audio/sfx_ctf_grab_pl.wv", },
|
||||
};
|
||||
static SOUND x2485[] = {
|
||||
/* x2485[0] */ { 0, "audio/sfx_ctf_grab_en.wv", },
|
||||
};
|
||||
static SOUND x2495[] = {
|
||||
/* x2495[0] */ { 0, "audio/sfx_ctf_cap_pl.wv", },
|
||||
};
|
||||
static SOUNDSET x9[] = {
|
||||
/* x9[0] */ { "gun_fire", 3,x1890, -1, },
|
||||
/* x9[1] */ { "shotgun_fire", 3,x1906, -1, },
|
||||
/* x9[2] */ { "grenade_fire", 3,x1922, -1, },
|
||||
/* x9[3] */ { "hammer_fire", 3,x1938, -1, },
|
||||
/* x9[4] */ { "hammer_hit", 3,x1954, -1, },
|
||||
/* x9[5] */ { "ninja_fire", 3,x1970, -1, },
|
||||
/* x9[6] */ { "grenade_explode", 3,x1986, -1, },
|
||||
/* x9[7] */ { "ninja_hit", 3,x2002, -1, },
|
||||
/* x9[8] */ { "rifle_fire", 3,x2018, -1, },
|
||||
/* x9[9] */ { "rifle_bounce", 3,x2034, -1, },
|
||||
/* x9[10] */ { "weapon_switch", 3,x2050, -1, },
|
||||
/* x9[11] */ { "player_pain_short", 12,x2066, -1, },
|
||||
/* x9[12] */ { "player_pain_long", 2,x2109, -1, },
|
||||
/* x9[13] */ { "body_land", 4,x2122, -1, },
|
||||
/* x9[14] */ { "player_airjump", 3,x2141, -1, },
|
||||
/* x9[15] */ { "player_jump", 8,x2157, -1, },
|
||||
/* x9[16] */ { "player_die", 3,x2188, -1, },
|
||||
/* x9[17] */ { "player_spawn", 7,x2204, -1, },
|
||||
/* x9[18] */ { "player_skid", 4,x2232, -1, },
|
||||
/* x9[19] */ { "tee_cry", 2,x2251, -1, },
|
||||
/* x9[20] */ { "hook_loop", 2,x2264, -1, },
|
||||
/* x9[21] */ { "hook_attach_ground", 3,x2277, -1, },
|
||||
/* x9[22] */ { "hook_attach_player", 3,x2293, -1, },
|
||||
/* x9[23] */ { "hook_noattach", 2,x2309, -1, },
|
||||
/* x9[24] */ { "pickup_health", 2,x2322, -1, },
|
||||
/* x9[25] */ { "pickup_armor", 4,x2335, -1, },
|
||||
/* x9[26] */ { "pickup_grenade", 1,x2354, -1, },
|
||||
/* x9[27] */ { "pickup_shotgun", 1,x2364, -1, },
|
||||
/* x9[28] */ { "pickup_ninja", 1,x2374, -1, },
|
||||
/* x9[29] */ { "weapon_spawn", 3,x2384, -1, },
|
||||
/* x9[30] */ { "weapon_noammo", 5,x2400, -1, },
|
||||
/* x9[31] */ { "hit", 2,x2422, -1, },
|
||||
/* x9[32] */ { "chat_server", 1,x2435, -1, },
|
||||
/* x9[33] */ { "chat_client", 1,x2445, -1, },
|
||||
/* x9[34] */ { "ctf_drop", 1,x2455, -1, },
|
||||
/* x9[35] */ { "ctf_return", 1,x2465, -1, },
|
||||
/* x9[36] */ { "ctf_grab_pl", 1,x2475, -1, },
|
||||
/* x9[37] */ { "ctf_grab_en", 1,x2485, -1, },
|
||||
/* x9[38] */ { "ctf_capture", 1,x2495, -1, },
|
||||
};
|
||||
static IMAGE x14[] = {
|
||||
/* x14[0] */ { "null", "", -1, },
|
||||
/* x14[1] */ { "game", "game.png", -1, },
|
||||
/* x14[2] */ { "particles", "particles.png", -1, },
|
||||
/* x14[3] */ { "cursor", "gui_cursor.png", -1, },
|
||||
/* x14[4] */ { "banner", "gui_logo.png", -1, },
|
||||
/* x14[5] */ { "emoticons", "emoticons.png", -1, },
|
||||
/* x14[6] */ { "browseicons", "browse_icons.png", -1, },
|
||||
/* x14[7] */ { "console_bg", "console.png", -1, },
|
||||
/* x14[8] */ { "console_bar", "console_bar.png", -1, },
|
||||
};
|
||||
static PICKUPSPEC x19[] = {
|
||||
/* x19[0] */ { "health", 15, 0, },
|
||||
/* x19[1] */ { "armor", 15, 0, },
|
||||
/* x19[2] */ { "weapon", 15, 0, },
|
||||
/* x19[3] */ { "ninja", 90, 90, },
|
||||
};
|
||||
static SPRITESET x28[] = {
|
||||
/* x28[0] */ { &x14[2], 8, 8, },
|
||||
/* x28[1] */ { &x14[1], 32, 16, },
|
||||
/* x28[2] */ { &x14[0], 8, 4, },
|
||||
/* x28[3] */ { &x14[6], 4, 1, },
|
||||
/* x28[4] */ { &x14[5], 4, 4, },
|
||||
};
|
||||
static SPRITE x44[] = {
|
||||
/* x44[0] */ { "part_slice", &x28[0], 0, 0, 1, 1, },
|
||||
/* x44[1] */ { "part_ball", &x28[0], 1, 0, 1, 1, },
|
||||
/* x44[2] */ { "part_splat01", &x28[0], 2, 0, 1, 1, },
|
||||
/* x44[3] */ { "part_splat02", &x28[0], 3, 0, 1, 1, },
|
||||
/* x44[4] */ { "part_splat03", &x28[0], 4, 0, 1, 1, },
|
||||
/* x44[5] */ { "part_smoke", &x28[0], 0, 1, 1, 1, },
|
||||
/* x44[6] */ { "part_shell", &x28[0], 0, 2, 2, 2, },
|
||||
/* x44[7] */ { "part_expl01", &x28[0], 0, 4, 4, 4, },
|
||||
/* x44[8] */ { "part_airjump", &x28[0], 2, 2, 2, 2, },
|
||||
/* x44[9] */ { "health_full", &x28[1], 21, 0, 2, 2, },
|
||||
/* x44[10] */ { "health_empty", &x28[1], 23, 0, 2, 2, },
|
||||
/* x44[11] */ { "armor_full", &x28[1], 21, 2, 2, 2, },
|
||||
/* x44[12] */ { "armor_empty", &x28[1], 23, 2, 2, 2, },
|
||||
/* x44[13] */ { "star1", &x28[1], 15, 0, 2, 2, },
|
||||
/* x44[14] */ { "star2", &x28[1], 17, 0, 2, 2, },
|
||||
/* x44[15] */ { "star3", &x28[1], 19, 0, 2, 2, },
|
||||
/* x44[16] */ { "part1", &x28[1], 6, 0, 1, 1, },
|
||||
/* x44[17] */ { "part2", &x28[1], 6, 1, 1, 1, },
|
||||
/* x44[18] */ { "part3", &x28[1], 7, 0, 1, 1, },
|
||||
/* x44[19] */ { "part4", &x28[1], 7, 1, 1, 1, },
|
||||
/* x44[20] */ { "part5", &x28[1], 8, 0, 1, 1, },
|
||||
/* x44[21] */ { "part6", &x28[1], 8, 1, 1, 1, },
|
||||
/* x44[22] */ { "part7", &x28[1], 9, 0, 2, 2, },
|
||||
/* x44[23] */ { "part8", &x28[1], 11, 0, 2, 2, },
|
||||
/* x44[24] */ { "part9", &x28[1], 13, 0, 2, 2, },
|
||||
/* x44[25] */ { "weapon_gun_body", &x28[1], 2, 4, 4, 2, },
|
||||
/* x44[26] */ { "weapon_gun_cursor", &x28[1], 0, 4, 2, 2, },
|
||||
/* x44[27] */ { "weapon_gun_proj", &x28[1], 6, 4, 2, 2, },
|
||||
/* x44[28] */ { "weapon_gun_muzzle1", &x28[1], 8, 4, 3, 2, },
|
||||
/* x44[29] */ { "weapon_gun_muzzle2", &x28[1], 12, 4, 3, 2, },
|
||||
/* x44[30] */ { "weapon_gun_muzzle3", &x28[1], 16, 4, 3, 2, },
|
||||
/* x44[31] */ { "weapon_shotgun_body", &x28[1], 2, 6, 8, 2, },
|
||||
/* x44[32] */ { "weapon_shotgun_cursor", &x28[1], 0, 6, 2, 2, },
|
||||
/* x44[33] */ { "weapon_shotgun_proj", &x28[1], 10, 6, 2, 2, },
|
||||
/* x44[34] */ { "weapon_shotgun_muzzle1", &x28[1], 12, 6, 3, 2, },
|
||||
/* x44[35] */ { "weapon_shotgun_muzzle2", &x28[1], 16, 6, 3, 2, },
|
||||
/* x44[36] */ { "weapon_shotgun_muzzle3", &x28[1], 20, 6, 3, 2, },
|
||||
/* x44[37] */ { "weapon_grenade_body", &x28[1], 2, 8, 7, 2, },
|
||||
/* x44[38] */ { "weapon_grenade_cursor", &x28[1], 0, 8, 2, 2, },
|
||||
/* x44[39] */ { "weapon_grenade_proj", &x28[1], 10, 8, 2, 2, },
|
||||
/* x44[40] */ { "weapon_hammer_body", &x28[1], 2, 1, 4, 3, },
|
||||
/* x44[41] */ { "weapon_hammer_cursor", &x28[1], 0, 0, 2, 2, },
|
||||
/* x44[42] */ { "weapon_hammer_proj", &x28[1], 0, 0, 0, 0, },
|
||||
/* x44[43] */ { "weapon_ninja_body", &x28[1], 2, 10, 7, 2, },
|
||||
/* x44[44] */ { "weapon_ninja_cursor", &x28[1], 0, 10, 2, 2, },
|
||||
/* x44[45] */ { "weapon_ninja_proj", &x28[1], 0, 0, 0, 0, },
|
||||
/* x44[46] */ { "weapon_rifle_body", &x28[1], 2, 12, 7, 3, },
|
||||
/* x44[47] */ { "weapon_rifle_cursor", &x28[1], 0, 12, 2, 2, },
|
||||
/* x44[48] */ { "weapon_rifle_proj", &x28[1], 10, 12, 2, 2, },
|
||||
/* x44[49] */ { "hook_chain", &x28[1], 2, 0, 1, 1, },
|
||||
/* x44[50] */ { "hook_head", &x28[1], 3, 0, 2, 1, },
|
||||
/* x44[51] */ { "weapon_ninja_muzzle1", &x28[1], 25, 0, 7, 4, },
|
||||
/* x44[52] */ { "weapon_ninja_muzzle2", &x28[1], 25, 4, 7, 4, },
|
||||
/* x44[53] */ { "weapon_ninja_muzzle3", &x28[1], 25, 8, 7, 4, },
|
||||
/* x44[54] */ { "pickup_health", &x28[1], 10, 2, 2, 2, },
|
||||
/* x44[55] */ { "pickup_armor", &x28[1], 12, 2, 2, 2, },
|
||||
/* x44[56] */ { "pickup_weapon", &x28[1], 3, 0, 6, 2, },
|
||||
/* x44[57] */ { "pickup_ninja", &x28[1], 3, 10, 7, 2, },
|
||||
/* x44[58] */ { "flag_blue", &x28[1], 12, 8, 4, 8, },
|
||||
/* x44[59] */ { "flag_red", &x28[1], 16, 8, 4, 8, },
|
||||
/* x44[60] */ { "tee_body", &x28[2], 0, 0, 3, 3, },
|
||||
/* x44[61] */ { "tee_body_outline", &x28[2], 3, 0, 3, 3, },
|
||||
/* x44[62] */ { "tee_foot", &x28[2], 6, 1, 2, 1, },
|
||||
/* x44[63] */ { "tee_foot_outline", &x28[2], 6, 2, 2, 1, },
|
||||
/* x44[64] */ { "tee_hand", &x28[2], 6, 0, 1, 1, },
|
||||
/* x44[65] */ { "tee_hand_outline", &x28[2], 7, 0, 1, 1, },
|
||||
/* x44[66] */ { "tee_eye_normal", &x28[2], 2, 3, 1, 1, },
|
||||
/* x44[67] */ { "tee_eye_angry", &x28[2], 3, 3, 1, 1, },
|
||||
/* x44[68] */ { "tee_eye_pain", &x28[2], 4, 3, 1, 1, },
|
||||
/* x44[69] */ { "tee_eye_happy", &x28[2], 5, 3, 1, 1, },
|
||||
/* x44[70] */ { "tee_eye_dead", &x28[2], 6, 3, 1, 1, },
|
||||
/* x44[71] */ { "tee_eye_surprise", &x28[2], 7, 3, 1, 1, },
|
||||
/* x44[72] */ { "oop", &x28[4], 0, 0, 1, 1, },
|
||||
/* x44[73] */ { "exclamation", &x28[4], 1, 0, 1, 1, },
|
||||
/* x44[74] */ { "hearts", &x28[4], 2, 0, 1, 1, },
|
||||
/* x44[75] */ { "drop", &x28[4], 3, 0, 1, 1, },
|
||||
/* x44[76] */ { "dotdot", &x28[4], 0, 1, 1, 1, },
|
||||
/* x44[77] */ { "music1", &x28[4], 1, 1, 1, 1, },
|
||||
/* x44[78] */ { "music2", &x28[4], 2, 1, 1, 1, },
|
||||
/* x44[79] */ { "ghost", &x28[4], 3, 1, 1, 1, },
|
||||
/* x44[80] */ { "sushi", &x28[4], 0, 2, 1, 1, },
|
||||
/* x44[81] */ { "splattee", &x28[4], 1, 2, 1, 1, },
|
||||
/* x44[82] */ { "deviltee", &x28[4], 2, 2, 1, 1, },
|
||||
/* x44[83] */ { "zomg", &x28[4], 3, 2, 1, 1, },
|
||||
/* x44[84] */ { "zzz", &x28[4], 0, 3, 1, 1, },
|
||||
/* x44[85] */ { "blank1", &x28[4], 1, 3, 1, 1, },
|
||||
/* x44[86] */ { "deadtee", &x28[4], 2, 3, 1, 1, },
|
||||
/* x44[87] */ { "blank2", &x28[4], 3, 3, 1, 1, },
|
||||
/* x44[88] */ { "browse_lock", &x28[3], 0, 0, 1, 1, },
|
||||
/* x44[89] */ { "browse_heart", &x28[3], 1, 0, 1, 1, },
|
||||
/* x44[90] */ { "browse_unpure", &x28[3], 3, 0, 1, 1, },
|
||||
};
|
||||
static ANIM_KEYFRAME x3965[] = {
|
||||
/* x3965[0] */ { 0.000000, 0.000000, -4.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME x3972[] = {
|
||||
/* x3972[0] */ { 0.000000, 0.000000, 10.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME x3979[] = {
|
||||
/* x3979[0] */ { 0.000000, 0.000000, 10.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME *x3986 = 0;
|
||||
static ANIM_KEYFRAME *x4010 = 0;
|
||||
static ANIM_KEYFRAME x4017[] = {
|
||||
/* x4017[0] */ { 0.000000, -7.000000, 0.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME x4024[] = {
|
||||
/* x4024[0] */ { 0.000000, 7.000000, 0.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME *x4031 = 0;
|
||||
static ANIM_KEYFRAME *x4050 = 0;
|
||||
static ANIM_KEYFRAME x4057[] = {
|
||||
/* x4057[0] */ { 0.000000, -3.000000, 0.000000, -0.100000, },
|
||||
};
|
||||
static ANIM_KEYFRAME x4064[] = {
|
||||
/* x4064[0] */ { 0.000000, 3.000000, 0.000000, -0.100000, },
|
||||
};
|
||||
static ANIM_KEYFRAME *x4071 = 0;
|
||||
static ANIM_KEYFRAME x4090[] = {
|
||||
/* x4090[0] */ { 0.000000, 0.000000, 0.000000, 0.000000, },
|
||||
/* x4090[1] */ { 0.200000, 0.000000, -1.000000, 0.000000, },
|
||||
/* x4090[2] */ { 0.400000, 0.000000, 0.000000, 0.000000, },
|
||||
/* x4090[3] */ { 0.600000, 0.000000, 0.000000, 0.000000, },
|
||||
/* x4090[4] */ { 0.800000, 0.000000, -1.000000, 0.000000, },
|
||||
/* x4090[5] */ { 1.000000, 0.000000, 0.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME x4097[] = {
|
||||
/* x4097[0] */ { 0.000000, 8.000000, 0.000000, 0.000000, },
|
||||
/* x4097[1] */ { 0.200000, -8.000000, 0.000000, 0.000000, },
|
||||
/* x4097[2] */ { 0.400000, -10.000000, -4.000000, 0.200000, },
|
||||
/* x4097[3] */ { 0.600000, -8.000000, -8.000000, 0.300000, },
|
||||
/* x4097[4] */ { 0.800000, 4.000000, -4.000000, -0.200000, },
|
||||
/* x4097[5] */ { 1.000000, 8.000000, 0.000000, 0.000000, },
|
||||
};
|
||||
static ANIM_KEYFRAME x4104[] = {
|
||||
/* x4104[0] */ { 0.000000, -10.000000, -4.000000, 0.200000, },
|
||||
/* x4104[1] */ { 0.200000, -8.000000, -8.000000, 0.300000, },
|
||||
/* x4104[2] */ { 0.400000, 4.000000, -4.000000, -0.200000, },
|
||||
/* x4104[3] */ { 0.600000, 8.000000, 0.000000, 0.000000, },
|
||||
/* x4104[4] */ { 0.800000, 8.000000, 0.000000, 0.000000, },
|
||||
/* x4104[5] */ { 1.000000, -10.000000, -4.000000, 0.200000, },
|
||||
};
|
||||
static ANIM_KEYFRAME *x4111 = 0;
|
||||
static ANIM_KEYFRAME *x4210 = 0;
|
||||
static ANIM_KEYFRAME *x4217 = 0;
|
||||
static ANIM_KEYFRAME *x4224 = 0;
|
||||
static ANIM_KEYFRAME x4231[] = {
|
||||
/* x4231[0] */ { 0.000000, 0.000000, 0.000000, -0.100000, },
|
||||
/* x4231[1] */ { 0.300000, 0.000000, 0.000000, 0.250000, },
|
||||
/* x4231[2] */ { 0.400000, 0.000000, 0.000000, 0.300000, },
|
||||
/* x4231[3] */ { 0.500000, 0.000000, 0.000000, 0.250000, },
|
||||
/* x4231[4] */ { 1.000000, 0.000000, 0.000000, -0.100000, },
|
||||
};
|
||||
static ANIM_KEYFRAME *x4265 = 0;
|
||||
static ANIM_KEYFRAME *x4272 = 0;
|
||||
static ANIM_KEYFRAME *x4279 = 0;
|
||||
static ANIM_KEYFRAME x4286[] = {
|
||||
/* x4286[0] */ { 0.000000, 0.000000, 0.000000, -0.250000, },
|
||||
/* x4286[1] */ { 0.100000, 0.000000, 0.000000, -0.050000, },
|
||||
/* x4286[2] */ { 0.150000, 0.000000, 0.000000, 0.350000, },
|
||||
/* x4286[3] */ { 0.420000, 0.000000, 0.000000, 0.400000, },
|
||||
/* x4286[4] */ { 0.500000, 0.000000, 0.000000, 0.350000, },
|
||||
/* x4286[5] */ { 1.000000, 0.000000, 0.000000, -0.250000, },
|
||||
};
|
||||
static ANIMATION x75[] = {
|
||||
/* x75[0] */ { "base", /* x75[0].body */ { 1,x3965, }, /* x75[0].back_foot */ { 1,x3972, }, /* x75[0].front_foot */ { 1,x3979, }, /* x75[0].attach */ { 0,x3986, }, },
|
||||
/* x75[1] */ { "idle", /* x75[1].body */ { 0,x4010, }, /* x75[1].back_foot */ { 1,x4017, }, /* x75[1].front_foot */ { 1,x4024, }, /* x75[1].attach */ { 0,x4031, }, },
|
||||
/* x75[2] */ { "inair", /* x75[2].body */ { 0,x4050, }, /* x75[2].back_foot */ { 1,x4057, }, /* x75[2].front_foot */ { 1,x4064, }, /* x75[2].attach */ { 0,x4071, }, },
|
||||
/* x75[3] */ { "walk", /* x75[3].body */ { 6,x4090, }, /* x75[3].back_foot */ { 6,x4097, }, /* x75[3].front_foot */ { 6,x4104, }, /* x75[3].attach */ { 0,x4111, }, },
|
||||
/* x75[4] */ { "hammer_swing", /* x75[4].body */ { 0,x4210, }, /* x75[4].back_foot */ { 0,x4217, }, /* x75[4].front_foot */ { 0,x4224, }, /* x75[4].attach */ { 5,x4231, }, },
|
||||
/* x75[5] */ { "ninja_swing", /* x75[5].body */ { 0,x4265, }, /* x75[5].back_foot */ { 0,x4272, }, /* x75[5].front_foot */ { 0,x4279, }, /* x75[5].attach */ { 6,x4286, }, },
|
||||
};
|
||||
static SPRITE* *x4443 = 0;
|
||||
static SPRITE* x4580[] = {
|
||||
&x44[28],
|
||||
&x44[29],
|
||||
&x44[30],
|
||||
};
|
||||
static SPRITE* x4765[] = {
|
||||
&x44[34],
|
||||
&x44[35],
|
||||
&x44[36],
|
||||
};
|
||||
static SPRITE* *x4950 = 0;
|
||||
static SPRITE* *x5087 = 0;
|
||||
static SPRITE* x5224[] = {
|
||||
&x44[51],
|
||||
&x44[52],
|
||||
&x44[53],
|
||||
};
|
||||
static WEAPONSPEC x1884[] = {
|
||||
/* x1884[0] */ { "hammer", &x44[40], &x44[41], &x44[42], 0,x4443, 96, 125, 10, 0, 3, 4.000000, -20.000000, 0.000000, 0.000000, 5.000000, },
|
||||
/* x1884[1] */ { "gun", &x44[25], &x44[26], &x44[27], 3,x4580, 64, 125, 10, 500, 1, 32.000000, -4.000000, 50.000000, 6.000000, 5.000000, },
|
||||
/* x1884[2] */ { "shotgun", &x44[31], &x44[32], &x44[33], 3,x4765, 96, 500, 10, 0, 1, 24.000000, -2.000000, 70.000000, 6.000000, 5.000000, },
|
||||
/* x1884[3] */ { "grenade", &x44[37], &x44[38], &x44[39], 0,x4950, 96, 500, 10, 0, 1, 24.000000, -2.000000, 0.000000, 0.000000, 5.000000, },
|
||||
/* x1884[4] */ { "rifle", &x44[46], &x44[47], &x44[48], 0,x5087, 92, 800, 10, 0, 5, 24.000000, -2.000000, 0.000000, 0.000000, 5.000000, },
|
||||
/* x1884[5] */ { "ninja", &x44[43], &x44[44], &x44[45], 3,x5224, 96, 800, 10, 0, 9, 0.000000, 0.000000, 40.000000, -4.000000, 5.000000, },
|
||||
};
|
||||
DATACONTAINER datacontainer =
|
||||
/* datacontainer */ {
|
||||
39,x9,
|
||||
9,x14,
|
||||
4,x19,
|
||||
5,x28,
|
||||
91,x44,
|
||||
6,x75,
|
||||
/* datacontainer.weapons */ { /* datacontainer.weapons.hammer */ { &x1884[0], }, /* datacontainer.weapons.gun */ { &x1884[1], }, /* datacontainer.weapons.shotgun */ { &x1884[2], 1.250000, 2200.000000, 0.800000, 0.250000, }, /* datacontainer.weapons.grenade */ { &x1884[3], 7.000000, 1000.000000, 2.000000, }, /* datacontainer.weapons.rifle */ { &x1884[4], 800.000000, 150, 1, 0.000000, }, /* datacontainer.weapons.ninja */ { &x1884[5], 15000, 200, 50, }, 6,x1884, },
|
||||
}
|
||||
;
|
||||
DATACONTAINER *data = &datacontainer;
|
||||
268
project/jni/application/teeworlds/src/game/generated/gc_data.hpp
Normal file
268
project/jni/application/teeworlds/src/game/generated/gc_data.hpp
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifndef CLIENT_CONTENT_HEADER
|
||||
#define CLIENT_CONTENT_HEADER
|
||||
struct SOUND
|
||||
{
|
||||
int id;
|
||||
const char* filename;
|
||||
};
|
||||
struct SOUNDSET
|
||||
{
|
||||
const char* name;
|
||||
int num_sounds;
|
||||
SOUND *sounds;
|
||||
int last;
|
||||
};
|
||||
struct IMAGE
|
||||
{
|
||||
const char* name;
|
||||
const char* filename;
|
||||
int id;
|
||||
};
|
||||
struct SPRITESET
|
||||
{
|
||||
IMAGE* image;
|
||||
int gridx;
|
||||
int gridy;
|
||||
};
|
||||
struct SPRITE
|
||||
{
|
||||
const char* name;
|
||||
SPRITESET* set;
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
};
|
||||
struct PICKUPSPEC
|
||||
{
|
||||
const char* name;
|
||||
int respawntime;
|
||||
int spawndelay;
|
||||
};
|
||||
struct ANIM_KEYFRAME
|
||||
{
|
||||
float time;
|
||||
float x;
|
||||
float y;
|
||||
float angle;
|
||||
};
|
||||
struct ANIM_SEQUENCE
|
||||
{
|
||||
int num_frames;
|
||||
ANIM_KEYFRAME *frames;
|
||||
};
|
||||
struct ANIMATION
|
||||
{
|
||||
const char* name;
|
||||
ANIM_SEQUENCE body;
|
||||
ANIM_SEQUENCE back_foot;
|
||||
ANIM_SEQUENCE front_foot;
|
||||
ANIM_SEQUENCE attach;
|
||||
};
|
||||
struct WEAPONSPEC
|
||||
{
|
||||
const char* name;
|
||||
SPRITE* sprite_body;
|
||||
SPRITE* sprite_cursor;
|
||||
SPRITE* sprite_proj;
|
||||
int num_sprite_muzzles;
|
||||
SPRITE* *sprite_muzzles;
|
||||
int visual_size;
|
||||
int firedelay;
|
||||
int maxammo;
|
||||
int ammoregentime;
|
||||
int damage;
|
||||
float offsetx;
|
||||
float offsety;
|
||||
float muzzleoffsetx;
|
||||
float muzzleoffsety;
|
||||
float muzzleduration;
|
||||
};
|
||||
struct WEAPONSPEC_HAMMER
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
};
|
||||
struct WEAPONSPEC_GUN
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float curvature;
|
||||
float speed;
|
||||
float lifetime;
|
||||
};
|
||||
struct WEAPONSPEC_SHOTGUN
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float curvature;
|
||||
float speed;
|
||||
float speeddiff;
|
||||
float lifetime;
|
||||
};
|
||||
struct WEAPONSPEC_GRENADE
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float curvature;
|
||||
float speed;
|
||||
float lifetime;
|
||||
};
|
||||
struct WEAPONSPEC_RIFLE
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float reach;
|
||||
int bounce_delay;
|
||||
int bounce_num;
|
||||
float bounce_cost;
|
||||
};
|
||||
struct WEAPONSPEC_NINJA
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
int duration;
|
||||
int movetime;
|
||||
int velocity;
|
||||
};
|
||||
struct WEAPONSPECS
|
||||
{
|
||||
WEAPONSPEC_HAMMER hammer;
|
||||
WEAPONSPEC_HAMMER gun;
|
||||
WEAPONSPEC_SHOTGUN shotgun;
|
||||
WEAPONSPEC_GRENADE grenade;
|
||||
WEAPONSPEC_RIFLE rifle;
|
||||
WEAPONSPEC_NINJA ninja;
|
||||
int num_id;
|
||||
WEAPONSPEC *id;
|
||||
};
|
||||
struct DATACONTAINER
|
||||
{
|
||||
int num_sounds;
|
||||
SOUNDSET *sounds;
|
||||
int num_images;
|
||||
IMAGE *images;
|
||||
int num_pickups;
|
||||
PICKUPSPEC *pickups;
|
||||
int num_spritesets;
|
||||
SPRITESET *spritesets;
|
||||
int num_sprites;
|
||||
SPRITE *sprites;
|
||||
int num_animations;
|
||||
ANIMATION *animations;
|
||||
WEAPONSPECS weapons;
|
||||
};
|
||||
extern DATACONTAINER *data;
|
||||
enum
|
||||
{
|
||||
IMAGE_NULL=0,
|
||||
IMAGE_GAME,
|
||||
IMAGE_PARTICLES,
|
||||
IMAGE_CURSOR,
|
||||
IMAGE_BANNER,
|
||||
IMAGE_EMOTICONS,
|
||||
IMAGE_BROWSEICONS,
|
||||
IMAGE_CONSOLE_BG,
|
||||
IMAGE_CONSOLE_BAR,
|
||||
NUM_IMAGES
|
||||
};
|
||||
enum
|
||||
{
|
||||
ANIM_BASE=0,
|
||||
ANIM_IDLE,
|
||||
ANIM_INAIR,
|
||||
ANIM_WALK,
|
||||
ANIM_HAMMER_SWING,
|
||||
ANIM_NINJA_SWING,
|
||||
NUM_ANIMS
|
||||
};
|
||||
enum
|
||||
{
|
||||
SPRITE_PART_SLICE=0,
|
||||
SPRITE_PART_BALL,
|
||||
SPRITE_PART_SPLAT01,
|
||||
SPRITE_PART_SPLAT02,
|
||||
SPRITE_PART_SPLAT03,
|
||||
SPRITE_PART_SMOKE,
|
||||
SPRITE_PART_SHELL,
|
||||
SPRITE_PART_EXPL01,
|
||||
SPRITE_PART_AIRJUMP,
|
||||
SPRITE_HEALTH_FULL,
|
||||
SPRITE_HEALTH_EMPTY,
|
||||
SPRITE_ARMOR_FULL,
|
||||
SPRITE_ARMOR_EMPTY,
|
||||
SPRITE_STAR1,
|
||||
SPRITE_STAR2,
|
||||
SPRITE_STAR3,
|
||||
SPRITE_PART1,
|
||||
SPRITE_PART2,
|
||||
SPRITE_PART3,
|
||||
SPRITE_PART4,
|
||||
SPRITE_PART5,
|
||||
SPRITE_PART6,
|
||||
SPRITE_PART7,
|
||||
SPRITE_PART8,
|
||||
SPRITE_PART9,
|
||||
SPRITE_WEAPON_GUN_BODY,
|
||||
SPRITE_WEAPON_GUN_CURSOR,
|
||||
SPRITE_WEAPON_GUN_PROJ,
|
||||
SPRITE_WEAPON_GUN_MUZZLE1,
|
||||
SPRITE_WEAPON_GUN_MUZZLE2,
|
||||
SPRITE_WEAPON_GUN_MUZZLE3,
|
||||
SPRITE_WEAPON_SHOTGUN_BODY,
|
||||
SPRITE_WEAPON_SHOTGUN_CURSOR,
|
||||
SPRITE_WEAPON_SHOTGUN_PROJ,
|
||||
SPRITE_WEAPON_SHOTGUN_MUZZLE1,
|
||||
SPRITE_WEAPON_SHOTGUN_MUZZLE2,
|
||||
SPRITE_WEAPON_SHOTGUN_MUZZLE3,
|
||||
SPRITE_WEAPON_GRENADE_BODY,
|
||||
SPRITE_WEAPON_GRENADE_CURSOR,
|
||||
SPRITE_WEAPON_GRENADE_PROJ,
|
||||
SPRITE_WEAPON_HAMMER_BODY,
|
||||
SPRITE_WEAPON_HAMMER_CURSOR,
|
||||
SPRITE_WEAPON_HAMMER_PROJ,
|
||||
SPRITE_WEAPON_NINJA_BODY,
|
||||
SPRITE_WEAPON_NINJA_CURSOR,
|
||||
SPRITE_WEAPON_NINJA_PROJ,
|
||||
SPRITE_WEAPON_RIFLE_BODY,
|
||||
SPRITE_WEAPON_RIFLE_CURSOR,
|
||||
SPRITE_WEAPON_RIFLE_PROJ,
|
||||
SPRITE_HOOK_CHAIN,
|
||||
SPRITE_HOOK_HEAD,
|
||||
SPRITE_WEAPON_NINJA_MUZZLE1,
|
||||
SPRITE_WEAPON_NINJA_MUZZLE2,
|
||||
SPRITE_WEAPON_NINJA_MUZZLE3,
|
||||
SPRITE_PICKUP_HEALTH,
|
||||
SPRITE_PICKUP_ARMOR,
|
||||
SPRITE_PICKUP_WEAPON,
|
||||
SPRITE_PICKUP_NINJA,
|
||||
SPRITE_FLAG_BLUE,
|
||||
SPRITE_FLAG_RED,
|
||||
SPRITE_TEE_BODY,
|
||||
SPRITE_TEE_BODY_OUTLINE,
|
||||
SPRITE_TEE_FOOT,
|
||||
SPRITE_TEE_FOOT_OUTLINE,
|
||||
SPRITE_TEE_HAND,
|
||||
SPRITE_TEE_HAND_OUTLINE,
|
||||
SPRITE_TEE_EYE_NORMAL,
|
||||
SPRITE_TEE_EYE_ANGRY,
|
||||
SPRITE_TEE_EYE_PAIN,
|
||||
SPRITE_TEE_EYE_HAPPY,
|
||||
SPRITE_TEE_EYE_DEAD,
|
||||
SPRITE_TEE_EYE_SURPRISE,
|
||||
SPRITE_OOP,
|
||||
SPRITE_EXCLAMATION,
|
||||
SPRITE_HEARTS,
|
||||
SPRITE_DROP,
|
||||
SPRITE_DOTDOT,
|
||||
SPRITE_MUSIC1,
|
||||
SPRITE_MUSIC2,
|
||||
SPRITE_GHOST,
|
||||
SPRITE_SUSHI,
|
||||
SPRITE_SPLATTEE,
|
||||
SPRITE_DEVILTEE,
|
||||
SPRITE_ZOMG,
|
||||
SPRITE_ZZZ,
|
||||
SPRITE_BLANK1,
|
||||
SPRITE_DEADTEE,
|
||||
SPRITE_BLANK2,
|
||||
SPRITE_BROWSE_LOCK,
|
||||
SPRITE_BROWSE_HEART,
|
||||
SPRITE_BROWSE_UNPURE,
|
||||
NUM_SPRITES
|
||||
};
|
||||
#endif
|
||||
268
project/jni/application/teeworlds/src/game/generated/gs_data.hpp
Normal file
268
project/jni/application/teeworlds/src/game/generated/gs_data.hpp
Normal file
@@ -0,0 +1,268 @@
|
||||
#ifndef SERVER_CONTENT_HEADER
|
||||
#define SERVER_CONTENT_HEADER
|
||||
struct SOUND
|
||||
{
|
||||
int id;
|
||||
const char* filename;
|
||||
};
|
||||
struct SOUNDSET
|
||||
{
|
||||
const char* name;
|
||||
int num_sounds;
|
||||
SOUND *sounds;
|
||||
int last;
|
||||
};
|
||||
struct IMAGE
|
||||
{
|
||||
const char* name;
|
||||
const char* filename;
|
||||
int id;
|
||||
};
|
||||
struct SPRITESET
|
||||
{
|
||||
IMAGE* image;
|
||||
int gridx;
|
||||
int gridy;
|
||||
};
|
||||
struct SPRITE
|
||||
{
|
||||
const char* name;
|
||||
SPRITESET* set;
|
||||
int x;
|
||||
int y;
|
||||
int w;
|
||||
int h;
|
||||
};
|
||||
struct PICKUPSPEC
|
||||
{
|
||||
const char* name;
|
||||
int respawntime;
|
||||
int spawndelay;
|
||||
};
|
||||
struct ANIM_KEYFRAME
|
||||
{
|
||||
float time;
|
||||
float x;
|
||||
float y;
|
||||
float angle;
|
||||
};
|
||||
struct ANIM_SEQUENCE
|
||||
{
|
||||
int num_frames;
|
||||
ANIM_KEYFRAME *frames;
|
||||
};
|
||||
struct ANIMATION
|
||||
{
|
||||
const char* name;
|
||||
ANIM_SEQUENCE body;
|
||||
ANIM_SEQUENCE back_foot;
|
||||
ANIM_SEQUENCE front_foot;
|
||||
ANIM_SEQUENCE attach;
|
||||
};
|
||||
struct WEAPONSPEC
|
||||
{
|
||||
const char* name;
|
||||
SPRITE* sprite_body;
|
||||
SPRITE* sprite_cursor;
|
||||
SPRITE* sprite_proj;
|
||||
int num_sprite_muzzles;
|
||||
SPRITE* *sprite_muzzles;
|
||||
int visual_size;
|
||||
int firedelay;
|
||||
int maxammo;
|
||||
int ammoregentime;
|
||||
int damage;
|
||||
float offsetx;
|
||||
float offsety;
|
||||
float muzzleoffsetx;
|
||||
float muzzleoffsety;
|
||||
float muzzleduration;
|
||||
};
|
||||
struct WEAPONSPEC_HAMMER
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
};
|
||||
struct WEAPONSPEC_GUN
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float curvature;
|
||||
float speed;
|
||||
float lifetime;
|
||||
};
|
||||
struct WEAPONSPEC_SHOTGUN
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float curvature;
|
||||
float speed;
|
||||
float speeddiff;
|
||||
float lifetime;
|
||||
};
|
||||
struct WEAPONSPEC_GRENADE
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float curvature;
|
||||
float speed;
|
||||
float lifetime;
|
||||
};
|
||||
struct WEAPONSPEC_RIFLE
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
float reach;
|
||||
int bounce_delay;
|
||||
int bounce_num;
|
||||
float bounce_cost;
|
||||
};
|
||||
struct WEAPONSPEC_NINJA
|
||||
{
|
||||
WEAPONSPEC* base;
|
||||
int duration;
|
||||
int movetime;
|
||||
int velocity;
|
||||
};
|
||||
struct WEAPONSPECS
|
||||
{
|
||||
WEAPONSPEC_HAMMER hammer;
|
||||
WEAPONSPEC_HAMMER gun;
|
||||
WEAPONSPEC_SHOTGUN shotgun;
|
||||
WEAPONSPEC_GRENADE grenade;
|
||||
WEAPONSPEC_RIFLE rifle;
|
||||
WEAPONSPEC_NINJA ninja;
|
||||
int num_id;
|
||||
WEAPONSPEC *id;
|
||||
};
|
||||
struct DATACONTAINER
|
||||
{
|
||||
int num_sounds;
|
||||
SOUNDSET *sounds;
|
||||
int num_images;
|
||||
IMAGE *images;
|
||||
int num_pickups;
|
||||
PICKUPSPEC *pickups;
|
||||
int num_spritesets;
|
||||
SPRITESET *spritesets;
|
||||
int num_sprites;
|
||||
SPRITE *sprites;
|
||||
int num_animations;
|
||||
ANIMATION *animations;
|
||||
WEAPONSPECS weapons;
|
||||
};
|
||||
extern DATACONTAINER *data;
|
||||
enum
|
||||
{
|
||||
IMAGE_NULL=0,
|
||||
IMAGE_GAME,
|
||||
IMAGE_PARTICLES,
|
||||
IMAGE_CURSOR,
|
||||
IMAGE_BANNER,
|
||||
IMAGE_EMOTICONS,
|
||||
IMAGE_BROWSEICONS,
|
||||
IMAGE_CONSOLE_BG,
|
||||
IMAGE_CONSOLE_BAR,
|
||||
NUM_IMAGES
|
||||
};
|
||||
enum
|
||||
{
|
||||
ANIM_BASE=0,
|
||||
ANIM_IDLE,
|
||||
ANIM_INAIR,
|
||||
ANIM_WALK,
|
||||
ANIM_HAMMER_SWING,
|
||||
ANIM_NINJA_SWING,
|
||||
NUM_ANIMS
|
||||
};
|
||||
enum
|
||||
{
|
||||
SPRITE_PART_SLICE=0,
|
||||
SPRITE_PART_BALL,
|
||||
SPRITE_PART_SPLAT01,
|
||||
SPRITE_PART_SPLAT02,
|
||||
SPRITE_PART_SPLAT03,
|
||||
SPRITE_PART_SMOKE,
|
||||
SPRITE_PART_SHELL,
|
||||
SPRITE_PART_EXPL01,
|
||||
SPRITE_PART_AIRJUMP,
|
||||
SPRITE_HEALTH_FULL,
|
||||
SPRITE_HEALTH_EMPTY,
|
||||
SPRITE_ARMOR_FULL,
|
||||
SPRITE_ARMOR_EMPTY,
|
||||
SPRITE_STAR1,
|
||||
SPRITE_STAR2,
|
||||
SPRITE_STAR3,
|
||||
SPRITE_PART1,
|
||||
SPRITE_PART2,
|
||||
SPRITE_PART3,
|
||||
SPRITE_PART4,
|
||||
SPRITE_PART5,
|
||||
SPRITE_PART6,
|
||||
SPRITE_PART7,
|
||||
SPRITE_PART8,
|
||||
SPRITE_PART9,
|
||||
SPRITE_WEAPON_GUN_BODY,
|
||||
SPRITE_WEAPON_GUN_CURSOR,
|
||||
SPRITE_WEAPON_GUN_PROJ,
|
||||
SPRITE_WEAPON_GUN_MUZZLE1,
|
||||
SPRITE_WEAPON_GUN_MUZZLE2,
|
||||
SPRITE_WEAPON_GUN_MUZZLE3,
|
||||
SPRITE_WEAPON_SHOTGUN_BODY,
|
||||
SPRITE_WEAPON_SHOTGUN_CURSOR,
|
||||
SPRITE_WEAPON_SHOTGUN_PROJ,
|
||||
SPRITE_WEAPON_SHOTGUN_MUZZLE1,
|
||||
SPRITE_WEAPON_SHOTGUN_MUZZLE2,
|
||||
SPRITE_WEAPON_SHOTGUN_MUZZLE3,
|
||||
SPRITE_WEAPON_GRENADE_BODY,
|
||||
SPRITE_WEAPON_GRENADE_CURSOR,
|
||||
SPRITE_WEAPON_GRENADE_PROJ,
|
||||
SPRITE_WEAPON_HAMMER_BODY,
|
||||
SPRITE_WEAPON_HAMMER_CURSOR,
|
||||
SPRITE_WEAPON_HAMMER_PROJ,
|
||||
SPRITE_WEAPON_NINJA_BODY,
|
||||
SPRITE_WEAPON_NINJA_CURSOR,
|
||||
SPRITE_WEAPON_NINJA_PROJ,
|
||||
SPRITE_WEAPON_RIFLE_BODY,
|
||||
SPRITE_WEAPON_RIFLE_CURSOR,
|
||||
SPRITE_WEAPON_RIFLE_PROJ,
|
||||
SPRITE_HOOK_CHAIN,
|
||||
SPRITE_HOOK_HEAD,
|
||||
SPRITE_WEAPON_NINJA_MUZZLE1,
|
||||
SPRITE_WEAPON_NINJA_MUZZLE2,
|
||||
SPRITE_WEAPON_NINJA_MUZZLE3,
|
||||
SPRITE_PICKUP_HEALTH,
|
||||
SPRITE_PICKUP_ARMOR,
|
||||
SPRITE_PICKUP_WEAPON,
|
||||
SPRITE_PICKUP_NINJA,
|
||||
SPRITE_FLAG_BLUE,
|
||||
SPRITE_FLAG_RED,
|
||||
SPRITE_TEE_BODY,
|
||||
SPRITE_TEE_BODY_OUTLINE,
|
||||
SPRITE_TEE_FOOT,
|
||||
SPRITE_TEE_FOOT_OUTLINE,
|
||||
SPRITE_TEE_HAND,
|
||||
SPRITE_TEE_HAND_OUTLINE,
|
||||
SPRITE_TEE_EYE_NORMAL,
|
||||
SPRITE_TEE_EYE_ANGRY,
|
||||
SPRITE_TEE_EYE_PAIN,
|
||||
SPRITE_TEE_EYE_HAPPY,
|
||||
SPRITE_TEE_EYE_DEAD,
|
||||
SPRITE_TEE_EYE_SURPRISE,
|
||||
SPRITE_OOP,
|
||||
SPRITE_EXCLAMATION,
|
||||
SPRITE_HEARTS,
|
||||
SPRITE_DROP,
|
||||
SPRITE_DOTDOT,
|
||||
SPRITE_MUSIC1,
|
||||
SPRITE_MUSIC2,
|
||||
SPRITE_GHOST,
|
||||
SPRITE_SUSHI,
|
||||
SPRITE_SPLATTEE,
|
||||
SPRITE_DEVILTEE,
|
||||
SPRITE_ZOMG,
|
||||
SPRITE_ZZZ,
|
||||
SPRITE_BLANK1,
|
||||
SPRITE_DEADTEE,
|
||||
SPRITE_BLANK2,
|
||||
SPRITE_BROWSE_LOCK,
|
||||
SPRITE_BROWSE_HEART,
|
||||
SPRITE_BROWSE_UNPURE,
|
||||
NUM_SPRITES
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#define GAME_NETVERSION_HASH "b67d1f1a1eea234e"
|
||||
57
project/jni/application/teeworlds/src/game/layers.cpp
Normal file
57
project/jni/application/teeworlds/src/game/layers.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#include <engine/e_common_interface.h>
|
||||
#include "layers.hpp"
|
||||
|
||||
static MAPITEM_LAYER_TILEMAP *game_layer = 0;
|
||||
static MAPITEM_GROUP *game_group = 0;
|
||||
|
||||
static int groups_start = 0;
|
||||
static int groups_num = 0;
|
||||
static int layers_start = 0;
|
||||
static int layers_num = 0;
|
||||
|
||||
void layers_init()
|
||||
{
|
||||
map_get_type(MAPITEMTYPE_GROUP, &groups_start, &groups_num);
|
||||
map_get_type(MAPITEMTYPE_LAYER, &layers_start, &layers_num);
|
||||
|
||||
for(int g = 0; g < layers_num_groups(); g++)
|
||||
{
|
||||
MAPITEM_GROUP *group = layers_get_group(g);
|
||||
for(int l = 0; l < group->num_layers; l++)
|
||||
{
|
||||
MAPITEM_LAYER *layer = layers_get_layer(group->start_layer+l);
|
||||
|
||||
if(layer->type == LAYERTYPE_TILES)
|
||||
{
|
||||
MAPITEM_LAYER_TILEMAP *tilemap = (MAPITEM_LAYER_TILEMAP *)layer;
|
||||
if(tilemap->flags&1)
|
||||
{
|
||||
game_layer = tilemap;
|
||||
game_group = group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int layers_num_groups() { return groups_num; }
|
||||
MAPITEM_GROUP *layers_get_group(int index)
|
||||
{
|
||||
return (MAPITEM_GROUP *)map_get_item(groups_start+index, 0, 0);
|
||||
}
|
||||
|
||||
MAPITEM_LAYER *layers_get_layer(int index)
|
||||
{
|
||||
return (MAPITEM_LAYER *)map_get_item(layers_start+index, 0, 0);
|
||||
}
|
||||
|
||||
MAPITEM_LAYER_TILEMAP *layers_game_layer()
|
||||
{
|
||||
return game_layer;
|
||||
}
|
||||
|
||||
MAPITEM_GROUP *layers_game_group()
|
||||
{
|
||||
return game_group;
|
||||
}
|
||||
|
||||
12
project/jni/application/teeworlds/src/game/layers.hpp
Normal file
12
project/jni/application/teeworlds/src/game/layers.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include "mapitems.hpp"
|
||||
|
||||
void layers_init();
|
||||
|
||||
MAPITEM_LAYER_TILEMAP *layers_game_layer();
|
||||
MAPITEM_GROUP *layers_game_group();
|
||||
|
||||
int layers_num_groups();
|
||||
MAPITEM_GROUP *layers_get_group(int index);
|
||||
MAPITEM_LAYER *layers_get_layer(int index);
|
||||
|
||||
|
||||
178
project/jni/application/teeworlds/src/game/mapitems.hpp
Normal file
178
project/jni/application/teeworlds/src/game/mapitems.hpp
Normal file
@@ -0,0 +1,178 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#ifndef GAME_MAPITEMS_H
|
||||
#define GAME_MAPITEMS_H
|
||||
|
||||
// layer types
|
||||
enum
|
||||
{
|
||||
LAYERTYPE_INVALID=0,
|
||||
LAYERTYPE_GAME, // not used
|
||||
LAYERTYPE_TILES,
|
||||
LAYERTYPE_QUADS,
|
||||
|
||||
MAPITEMTYPE_VERSION=0,
|
||||
MAPITEMTYPE_INFO,
|
||||
MAPITEMTYPE_IMAGE,
|
||||
MAPITEMTYPE_ENVELOPE,
|
||||
MAPITEMTYPE_GROUP,
|
||||
MAPITEMTYPE_LAYER,
|
||||
MAPITEMTYPE_ENVPOINTS,
|
||||
|
||||
|
||||
CURVETYPE_STEP=0,
|
||||
CURVETYPE_LINEAR,
|
||||
CURVETYPE_SLOW,
|
||||
CURVETYPE_FAST,
|
||||
CURVETYPE_SMOOTH,
|
||||
NUM_CURVETYPES,
|
||||
|
||||
// game layer tiles
|
||||
ENTITY_NULL=0,
|
||||
ENTITY_SPAWN,
|
||||
ENTITY_SPAWN_RED,
|
||||
ENTITY_SPAWN_BLUE,
|
||||
ENTITY_FLAGSTAND_RED,
|
||||
ENTITY_FLAGSTAND_BLUE,
|
||||
ENTITY_ARMOR_1,
|
||||
ENTITY_HEALTH_1,
|
||||
ENTITY_WEAPON_SHOTGUN,
|
||||
ENTITY_WEAPON_GRENADE,
|
||||
ENTITY_POWERUP_NINJA,
|
||||
ENTITY_WEAPON_RIFLE,
|
||||
NUM_ENTITIES,
|
||||
|
||||
TILE_AIR=0,
|
||||
TILE_SOLID,
|
||||
TILE_DEATH,
|
||||
TILE_NOHOOK,
|
||||
|
||||
TILEFLAG_VFLIP=1,
|
||||
TILEFLAG_HFLIP=2,
|
||||
TILEFLAG_OPAQUE=4,
|
||||
|
||||
LAYERFLAG_DETAIL=1,
|
||||
|
||||
ENTITY_OFFSET=255-16*4,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x, y; // 22.10 fixed point
|
||||
} POINT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int r, g, b, a;
|
||||
} COLOR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
POINT points[5];
|
||||
COLOR colors[4];
|
||||
POINT texcoords[4];
|
||||
|
||||
int pos_env;
|
||||
int pos_env_offset;
|
||||
|
||||
int color_env;
|
||||
int color_env_offset;
|
||||
} QUAD;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char index;
|
||||
unsigned char flags;
|
||||
unsigned char skip;
|
||||
unsigned char reserved;
|
||||
} TILE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
int width;
|
||||
int height;
|
||||
int external;
|
||||
int image_name;
|
||||
int image_data;
|
||||
} MAPITEM_IMAGE;
|
||||
|
||||
struct MAPITEM_GROUP_v1
|
||||
{
|
||||
int version;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int parallax_x;
|
||||
int parallax_y;
|
||||
|
||||
int start_layer;
|
||||
int num_layers;
|
||||
} ;
|
||||
|
||||
|
||||
struct MAPITEM_GROUP : public MAPITEM_GROUP_v1
|
||||
{
|
||||
enum { CURRENT_VERSION=2 };
|
||||
|
||||
int use_clipping;
|
||||
int clip_x;
|
||||
int clip_y;
|
||||
int clip_w;
|
||||
int clip_h;
|
||||
} ;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
int type;
|
||||
int flags;
|
||||
} MAPITEM_LAYER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MAPITEM_LAYER layer;
|
||||
int version;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
int flags;
|
||||
|
||||
COLOR color;
|
||||
int color_env;
|
||||
int color_env_offset;
|
||||
|
||||
int image;
|
||||
int data;
|
||||
} MAPITEM_LAYER_TILEMAP;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MAPITEM_LAYER layer;
|
||||
int version;
|
||||
|
||||
int num_quads;
|
||||
int data;
|
||||
int image;
|
||||
} MAPITEM_LAYER_QUADS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
} MAPITEM_VERSION;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int time; // in ms
|
||||
int curvetype;
|
||||
int values[4]; // 1-4 depending on envelope (22.10 fixed point)
|
||||
} ENVPOINT;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int version;
|
||||
int channels;
|
||||
int start_point;
|
||||
int num_points;
|
||||
int name;
|
||||
} MAPITEM_ENVELOPE;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,838 @@
|
||||
#include <new>
|
||||
#include <engine/e_server_interface.h>
|
||||
#include <engine/e_config.h>
|
||||
#include <game/server/gamecontext.hpp>
|
||||
#include <game/mapitems.hpp>
|
||||
|
||||
#include "character.hpp"
|
||||
#include "laser.hpp"
|
||||
#include "projectile.hpp"
|
||||
|
||||
struct INPUT_COUNT
|
||||
{
|
||||
int presses;
|
||||
int releases;
|
||||
};
|
||||
|
||||
static INPUT_COUNT count_input(int prev, int cur)
|
||||
{
|
||||
INPUT_COUNT c = {0,0};
|
||||
prev &= INPUT_STATE_MASK;
|
||||
cur &= INPUT_STATE_MASK;
|
||||
int i = prev;
|
||||
while(i != cur)
|
||||
{
|
||||
i = (i+1)&INPUT_STATE_MASK;
|
||||
if(i&1)
|
||||
c.presses++;
|
||||
else
|
||||
c.releases++;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
MACRO_ALLOC_POOL_ID_IMPL(CHARACTER, MAX_CLIENTS)
|
||||
|
||||
// player
|
||||
CHARACTER::CHARACTER()
|
||||
: ENTITY(NETOBJTYPE_CHARACTER)
|
||||
{
|
||||
proximity_radius = phys_size;
|
||||
}
|
||||
|
||||
void CHARACTER::reset()
|
||||
{
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool CHARACTER::spawn(PLAYER *player, vec2 pos, int team)
|
||||
{
|
||||
player_state = PLAYERSTATE_UNKNOWN;
|
||||
emote_stop = -1;
|
||||
last_action = -1;
|
||||
active_weapon = WEAPON_GUN;
|
||||
last_weapon = WEAPON_HAMMER;
|
||||
queued_weapon = -1;
|
||||
|
||||
//clear();
|
||||
this->player = player;
|
||||
this->pos = pos;
|
||||
this->team = team;
|
||||
|
||||
core.reset();
|
||||
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;
|
||||
player->force_balanced = false;
|
||||
|
||||
game.controller->on_character_spawn(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHARACTER::destroy()
|
||||
{
|
||||
game.world.core.characters[player->client_id] = 0;
|
||||
alive = false;
|
||||
}
|
||||
|
||||
void CHARACTER::set_weapon(int w)
|
||||
{
|
||||
if(w == active_weapon)
|
||||
return;
|
||||
|
||||
last_weapon = active_weapon;
|
||||
queued_weapon = -1;
|
||||
active_weapon = w;
|
||||
if(active_weapon < 0 || active_weapon >= NUM_WEAPONS)
|
||||
active_weapon = 0;
|
||||
|
||||
game.create_sound(pos, SOUND_WEAPON_SWITCH);
|
||||
}
|
||||
|
||||
bool CHARACTER::is_grounded()
|
||||
{
|
||||
if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
|
||||
return true;
|
||||
if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int CHARACTER::handle_ninja()
|
||||
{
|
||||
if(active_weapon != WEAPON_NINJA)
|
||||
return 0;
|
||||
|
||||
vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
|
||||
|
||||
if ((server_tick() - ninja.activationtick) > (data->weapons.ninja.duration * server_tickspeed() / 1000))
|
||||
{
|
||||
// time's up, return
|
||||
weapons[WEAPON_NINJA].got = false;
|
||||
active_weapon = last_weapon;
|
||||
if(active_weapon == WEAPON_NINJA)
|
||||
active_weapon = WEAPON_GUN;
|
||||
set_weapon(active_weapon);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// force ninja weapon
|
||||
set_weapon(WEAPON_NINJA);
|
||||
|
||||
ninja.currentmovetime--;
|
||||
|
||||
if (ninja.currentmovetime == 0)
|
||||
{
|
||||
// reset player velocity
|
||||
core.vel *= 0.2f;
|
||||
//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
|
||||
}
|
||||
|
||||
if (ninja.currentmovetime > 0)
|
||||
{
|
||||
// Set player velocity
|
||||
core.vel = ninja.activationdir * data->weapons.ninja.velocity;
|
||||
vec2 oldpos = pos;
|
||||
move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f);
|
||||
// reset velocity so the client doesn't predict stuff
|
||||
core.vel = vec2(0.0f,0.0f);
|
||||
if ((ninja.currentmovetime % 2) == 0)
|
||||
{
|
||||
//create_smoke(pos);
|
||||
}
|
||||
|
||||
// check if we hit anything along the way
|
||||
{
|
||||
CHARACTER *ents[64];
|
||||
vec2 dir = pos - oldpos;
|
||||
float radius = phys_size * 2.0f; //length(dir * 0.5f);
|
||||
vec2 center = oldpos + dir * 0.5f;
|
||||
int num = game.world.find_entities(center, radius, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER);
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
// Check if entity is a player
|
||||
if (ents[i] == this)
|
||||
continue;
|
||||
// make sure we haven't hit this object before
|
||||
bool balreadyhit = false;
|
||||
for (int j = 0; j < numobjectshit; j++)
|
||||
{
|
||||
if (hitobjects[j] == ents[i])
|
||||
balreadyhit = true;
|
||||
}
|
||||
if (balreadyhit)
|
||||
continue;
|
||||
|
||||
// check so we are sufficiently close
|
||||
if (distance(ents[i]->pos, pos) > (phys_size * 2.0f))
|
||||
continue;
|
||||
|
||||
// hit a player, give him damage and stuffs...
|
||||
game.create_sound(ents[i]->pos, SOUND_NINJA_HIT);
|
||||
// set his velocity to fast upward (for now)
|
||||
if(numobjectshit < 10)
|
||||
hitobjects[numobjectshit++] = ents[i];
|
||||
|
||||
ents[i]->take_damage(vec2(0,10.0f), data->weapons.ninja.base->damage, player->client_id,WEAPON_NINJA);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void CHARACTER::do_weaponswitch()
|
||||
{
|
||||
if(reload_timer != 0) // make sure we have reloaded
|
||||
return;
|
||||
|
||||
if(queued_weapon == -1) // check for a queued weapon
|
||||
return;
|
||||
|
||||
if(weapons[WEAPON_NINJA].got) // if we have ninja, no weapon selection is possible
|
||||
return;
|
||||
|
||||
// switch weapon
|
||||
set_weapon(queued_weapon);
|
||||
}
|
||||
|
||||
void CHARACTER::handle_weaponswitch()
|
||||
{
|
||||
int wanted_weapon = active_weapon;
|
||||
if(queued_weapon != -1)
|
||||
wanted_weapon = queued_weapon;
|
||||
|
||||
// select weapon
|
||||
int next = count_input(latest_previnput.next_weapon, latest_input.next_weapon).presses;
|
||||
int prev = count_input(latest_previnput.prev_weapon, latest_input.prev_weapon).presses;
|
||||
|
||||
if(next < 128) // make sure we only try sane stuff
|
||||
{
|
||||
while(next) // next weapon selection
|
||||
{
|
||||
wanted_weapon = (wanted_weapon+1)%NUM_WEAPONS;
|
||||
if(weapons[wanted_weapon].got)
|
||||
next--;
|
||||
}
|
||||
}
|
||||
|
||||
if(prev < 128) // make sure we only try sane stuff
|
||||
{
|
||||
while(prev) // prev weapon selection
|
||||
{
|
||||
wanted_weapon = (wanted_weapon-1)<0?NUM_WEAPONS-1:wanted_weapon-1;
|
||||
if(weapons[wanted_weapon].got)
|
||||
prev--;
|
||||
}
|
||||
}
|
||||
|
||||
// direct weapon selection
|
||||
if(latest_input.wanted_weapon)
|
||||
wanted_weapon = input.wanted_weapon-1;
|
||||
|
||||
// check for insane values
|
||||
if(wanted_weapon >= 0 && wanted_weapon < NUM_WEAPONS && wanted_weapon != active_weapon && weapons[wanted_weapon].got)
|
||||
queued_weapon = wanted_weapon;
|
||||
|
||||
do_weaponswitch();
|
||||
}
|
||||
|
||||
void CHARACTER::fire_weapon()
|
||||
{
|
||||
if(reload_timer != 0)
|
||||
return;
|
||||
|
||||
do_weaponswitch();
|
||||
|
||||
vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
|
||||
|
||||
bool fullauto = false;
|
||||
if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN || active_weapon == WEAPON_RIFLE)
|
||||
fullauto = true;
|
||||
|
||||
|
||||
// check if we gonna fire
|
||||
bool will_fire = false;
|
||||
if(count_input(latest_previnput.fire, latest_input.fire).presses) will_fire = true;
|
||||
if(fullauto && (latest_input.fire&1) && weapons[active_weapon].ammo) will_fire = true;
|
||||
if(!will_fire)
|
||||
return;
|
||||
|
||||
// check for ammo
|
||||
if(!weapons[active_weapon].ammo)
|
||||
{
|
||||
// 125ms is a magical limit of how fast a human can click
|
||||
reload_timer = 125 * server_tickspeed() / 1000;;
|
||||
game.create_sound(pos, SOUND_WEAPON_NOAMMO);
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 projectile_startpos = pos+direction*phys_size*0.75f;
|
||||
|
||||
switch(active_weapon)
|
||||
{
|
||||
case WEAPON_HAMMER:
|
||||
{
|
||||
// reset objects hit
|
||||
numobjectshit = 0;
|
||||
game.create_sound(pos, SOUND_HAMMER_FIRE);
|
||||
|
||||
CHARACTER *ents[64];
|
||||
int hits = 0;
|
||||
int num = game.world.find_entities(pos+direction*phys_size*0.75f, phys_size*0.5f, (ENTITY**)ents, 64, NETOBJTYPE_CHARACTER);
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
CHARACTER *target = ents[i];
|
||||
if (target == this)
|
||||
continue;
|
||||
|
||||
// hit a player, give him damage and stuffs...
|
||||
vec2 fdir = normalize(ents[i]->pos - pos);
|
||||
|
||||
// set his velocity to fast upward (for now)
|
||||
game.create_hammerhit(pos);
|
||||
ents[i]->take_damage(vec2(0,-1.0f), data->weapons.hammer.base->damage, player->client_id, active_weapon);
|
||||
vec2 dir;
|
||||
if (length(target->pos - pos) > 0.0f)
|
||||
dir = normalize(target->pos - pos);
|
||||
else
|
||||
dir = vec2(0,-1);
|
||||
|
||||
target->core.vel += normalize(dir + vec2(0,-1.1f)) * 10.0f;
|
||||
hits++;
|
||||
}
|
||||
|
||||
// if we hit anything, we have to wait for the reload
|
||||
if(hits)
|
||||
reload_timer = server_tickspeed()/3;
|
||||
|
||||
} break;
|
||||
|
||||
case WEAPON_GUN:
|
||||
{
|
||||
PROJECTILE *proj = new PROJECTILE(WEAPON_GUN,
|
||||
player->client_id,
|
||||
projectile_startpos,
|
||||
direction,
|
||||
(int)(server_tickspeed()*tuning.gun_lifetime),
|
||||
1, 0, 0, -1, WEAPON_GUN);
|
||||
|
||||
// pack the projectile and send it to the client directly
|
||||
NETOBJ_PROJECTILE p;
|
||||
proj->fill_info(&p);
|
||||
|
||||
msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
|
||||
msg_pack_int(1);
|
||||
for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
|
||||
msg_pack_int(((int *)&p)[i]);
|
||||
msg_pack_end();
|
||||
server_send_msg(player->client_id);
|
||||
|
||||
game.create_sound(pos, SOUND_GUN_FIRE);
|
||||
} break;
|
||||
|
||||
case WEAPON_SHOTGUN:
|
||||
{
|
||||
int shotspread = 2;
|
||||
|
||||
msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
|
||||
msg_pack_int(shotspread*2+1);
|
||||
|
||||
for(int i = -shotspread; i <= shotspread; i++)
|
||||
{
|
||||
float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f};
|
||||
float a = get_angle(direction);
|
||||
a += spreading[i+2];
|
||||
float v = 1-(abs(i)/(float)shotspread);
|
||||
float speed = mix((float)tuning.shotgun_speeddiff, 1.0f, v);
|
||||
PROJECTILE *proj = new PROJECTILE(WEAPON_SHOTGUN,
|
||||
player->client_id,
|
||||
projectile_startpos,
|
||||
vec2(cosf(a), sinf(a))*speed,
|
||||
(int)(server_tickspeed()*tuning.shotgun_lifetime),
|
||||
1, 0, 0, -1, WEAPON_SHOTGUN);
|
||||
|
||||
// pack the projectile and send it to the client directly
|
||||
NETOBJ_PROJECTILE p;
|
||||
proj->fill_info(&p);
|
||||
|
||||
for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
|
||||
msg_pack_int(((int *)&p)[i]);
|
||||
}
|
||||
|
||||
msg_pack_end();
|
||||
server_send_msg(player->client_id);
|
||||
|
||||
game.create_sound(pos, SOUND_SHOTGUN_FIRE);
|
||||
} break;
|
||||
|
||||
case WEAPON_GRENADE:
|
||||
{
|
||||
PROJECTILE *proj = new PROJECTILE(WEAPON_GRENADE,
|
||||
player->client_id,
|
||||
projectile_startpos,
|
||||
direction,
|
||||
(int)(server_tickspeed()*tuning.grenade_lifetime),
|
||||
1, PROJECTILE::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE);
|
||||
|
||||
// pack the projectile and send it to the client directly
|
||||
NETOBJ_PROJECTILE p;
|
||||
proj->fill_info(&p);
|
||||
|
||||
msg_pack_start(NETMSGTYPE_SV_EXTRAPROJECTILE, 0);
|
||||
msg_pack_int(1);
|
||||
for(unsigned i = 0; i < sizeof(NETOBJ_PROJECTILE)/sizeof(int); i++)
|
||||
msg_pack_int(((int *)&p)[i]);
|
||||
msg_pack_end();
|
||||
server_send_msg(player->client_id);
|
||||
|
||||
game.create_sound(pos, SOUND_GRENADE_FIRE);
|
||||
} break;
|
||||
|
||||
case WEAPON_RIFLE:
|
||||
{
|
||||
new LASER(pos, direction, tuning.laser_reach, player->client_id);
|
||||
game.create_sound(pos, SOUND_RIFLE_FIRE);
|
||||
} break;
|
||||
|
||||
case WEAPON_NINJA:
|
||||
{
|
||||
attack_tick = server_tick();
|
||||
ninja.activationdir = direction;
|
||||
ninja.currentmovetime = data->weapons.ninja.movetime * server_tickspeed() / 1000;
|
||||
|
||||
//reload_timer = data->weapons.ninja.base->firedelay * server_tickspeed() / 1000 + server_tick();
|
||||
|
||||
// reset hit objects
|
||||
numobjectshit = 0;
|
||||
|
||||
game.create_sound(pos, SOUND_NINJA_FIRE);
|
||||
|
||||
} break;
|
||||
|
||||
}
|
||||
|
||||
if(weapons[active_weapon].ammo > 0) // -1 == unlimited
|
||||
weapons[active_weapon].ammo--;
|
||||
attack_tick = server_tick();
|
||||
if(!reload_timer)
|
||||
reload_timer = data->weapons.id[active_weapon].firedelay * server_tickspeed() / 1000;
|
||||
}
|
||||
|
||||
int CHARACTER::handle_weapons()
|
||||
{
|
||||
vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
|
||||
|
||||
/*
|
||||
if(config.dbg_stress)
|
||||
{
|
||||
for(int i = 0; i < NUM_WEAPONS; i++)
|
||||
{
|
||||
weapons[i].got = true;
|
||||
weapons[i].ammo = 10;
|
||||
}
|
||||
|
||||
if(reload_timer) // twice as fast reload
|
||||
reload_timer--;
|
||||
} */
|
||||
|
||||
//if(active_weapon == WEAPON_NINJA)
|
||||
handle_ninja();
|
||||
|
||||
|
||||
// check reload timer
|
||||
if(reload_timer)
|
||||
{
|
||||
reload_timer--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
if (active_weapon == WEAPON_NINJA)
|
||||
{
|
||||
// don't update other weapons while ninja is active
|
||||
return handle_ninja();
|
||||
}*/
|
||||
|
||||
// fire weapon, if wanted
|
||||
fire_weapon();
|
||||
|
||||
// ammo regen
|
||||
int ammoregentime = data->weapons.id[active_weapon].ammoregentime;
|
||||
if(ammoregentime)
|
||||
{
|
||||
// If equipped and not active, regen ammo?
|
||||
if (reload_timer <= 0)
|
||||
{
|
||||
if (weapons[active_weapon].ammoregenstart < 0)
|
||||
weapons[active_weapon].ammoregenstart = server_tick();
|
||||
|
||||
if ((server_tick() - weapons[active_weapon].ammoregenstart) >= ammoregentime * server_tickspeed() / 1000)
|
||||
{
|
||||
// Add some ammo
|
||||
weapons[active_weapon].ammo = min(weapons[active_weapon].ammo + 1, 10);
|
||||
weapons[active_weapon].ammoregenstart = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
weapons[active_weapon].ammoregenstart = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CHARACTER::on_predicted_input(NETOBJ_PLAYER_INPUT *new_input)
|
||||
{
|
||||
// check for changes
|
||||
if(mem_comp(&input, new_input, sizeof(NETOBJ_PLAYER_INPUT)) != 0)
|
||||
last_action = server_tick();
|
||||
|
||||
// copy new input
|
||||
mem_copy(&input, new_input, sizeof(input));
|
||||
num_inputs++;
|
||||
|
||||
// or are not allowed to aim in the center
|
||||
if(input.target_x == 0 && input.target_y == 0)
|
||||
input.target_y = -1;
|
||||
}
|
||||
|
||||
void CHARACTER::on_direct_input(NETOBJ_PLAYER_INPUT *new_input)
|
||||
{
|
||||
mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
|
||||
mem_copy(&latest_input, new_input, sizeof(latest_input));
|
||||
|
||||
if(num_inputs > 2 && team != -1)
|
||||
{
|
||||
handle_weaponswitch();
|
||||
fire_weapon();
|
||||
}
|
||||
|
||||
mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
|
||||
}
|
||||
|
||||
void CHARACTER::tick()
|
||||
{
|
||||
if(player->force_balanced)
|
||||
{
|
||||
char buf[128];
|
||||
str_format(buf, sizeof(buf), "You were moved to %s due to team balancing", game.controller->get_team_name(team));
|
||||
game.send_broadcast(buf, player->client_id);
|
||||
|
||||
player->force_balanced = false;
|
||||
}
|
||||
|
||||
//player_core core;
|
||||
//core.pos = pos;
|
||||
//core.jumped = jumped;
|
||||
core.input = input;
|
||||
core.tick(true);
|
||||
|
||||
float phys_size = 28.0f;
|
||||
// handle death-tiles
|
||||
if(col_get((int)(pos.x+phys_size/2), (int)(pos.y-phys_size/2))&COLFLAG_DEATH ||
|
||||
col_get((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2))&COLFLAG_DEATH ||
|
||||
col_get((int)(pos.x-phys_size/2), (int)(pos.y-phys_size/2))&COLFLAG_DEATH ||
|
||||
col_get((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2))&COLFLAG_DEATH)
|
||||
{
|
||||
die(player->client_id, WEAPON_WORLD);
|
||||
}
|
||||
|
||||
// handle weapons
|
||||
handle_weapons();
|
||||
|
||||
player_state = input.player_state;
|
||||
|
||||
// Previnput
|
||||
previnput = input;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
vec2 start_vel = core.vel;
|
||||
bool stuck_before = test_box(core.pos, vec2(28.0f, 28.0f));
|
||||
|
||||
core.move();
|
||||
bool stuck_after_move = test_box(core.pos, vec2(28.0f, 28.0f));
|
||||
core.quantize();
|
||||
bool stuck_after_quant = test_box(core.pos, vec2(28.0f, 28.0f));
|
||||
pos = core.pos;
|
||||
|
||||
if(!stuck_before && (stuck_after_move || stuck_after_quant))
|
||||
{
|
||||
dbg_msg("player", "STUCK!!! %d %d %d %f %f %f %f %x %x %x %x",
|
||||
stuck_before,
|
||||
stuck_after_move,
|
||||
stuck_after_quant,
|
||||
start_pos.x, start_pos.y,
|
||||
start_vel.x, start_vel.y,
|
||||
*((unsigned *)&start_pos.x), *((unsigned *)&start_pos.y),
|
||||
*((unsigned *)&start_vel.x), *((unsigned *)&start_vel.y));
|
||||
}
|
||||
|
||||
int events = core.triggered_events;
|
||||
int mask = cmask_all_except_one(player->client_id);
|
||||
|
||||
if(events&COREEVENT_GROUND_JUMP) game.create_sound(pos, SOUND_PLAYER_JUMP, mask);
|
||||
|
||||
//if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
|
||||
if(events&COREEVENT_HOOK_ATTACH_PLAYER) game.create_sound(pos, SOUND_HOOK_ATTACH_PLAYER, cmask_all());
|
||||
if(events&COREEVENT_HOOK_ATTACH_GROUND) game.create_sound(pos, SOUND_HOOK_ATTACH_GROUND, mask);
|
||||
if(events&COREEVENT_HOOK_HIT_NOHOOK) game.create_sound(pos, SOUND_HOOK_NOATTACH, mask);
|
||||
//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
|
||||
//}
|
||||
|
||||
if(team == -1)
|
||||
{
|
||||
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);
|
||||
|
||||
// only allow dead reackoning for a top of 3 seconds
|
||||
if(reckoning_tick+server_tickspeed()*3 < server_tick() || mem_comp(&predicted, ¤t, sizeof(NETOBJ_CHARACTER)) != 0)
|
||||
{
|
||||
reckoning_tick = server_tick();
|
||||
sendcore = core;
|
||||
reckoningcore = core;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CHARACTER::increase_health(int amount)
|
||||
{
|
||||
if(health >= 10)
|
||||
return false;
|
||||
health = clamp(health+amount, 0, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CHARACTER::increase_armor(int amount)
|
||||
{
|
||||
if(armor >= 10)
|
||||
return false;
|
||||
armor = clamp(armor+amount, 0, 10);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHARACTER::die(int killer, int weapon)
|
||||
{
|
||||
/*if (dead || team == -1)
|
||||
return;*/
|
||||
int mode_special = game.controller->on_character_death(this, game.players[killer], weapon);
|
||||
|
||||
dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d special=%d",
|
||||
killer, server_clientname(killer),
|
||||
player->client_id, server_clientname(player->client_id), weapon, mode_special);
|
||||
|
||||
// send the kill message
|
||||
NETMSG_SV_KILLMSG msg;
|
||||
msg.killer = killer;
|
||||
msg.victim = player->client_id;
|
||||
msg.weapon = weapon;
|
||||
msg.mode_special = mode_special;
|
||||
msg.pack(MSGFLAG_VITAL);
|
||||
server_send_msg(-1);
|
||||
|
||||
// a nice sound
|
||||
game.create_sound(pos, SOUND_PLAYER_DIE);
|
||||
|
||||
// set dead state
|
||||
// TODO: do stuff here
|
||||
/*
|
||||
die_pos = pos;
|
||||
dead = true;
|
||||
*/
|
||||
|
||||
// this is for auto respawn after 3 secs
|
||||
player->die_tick = server_tick();
|
||||
|
||||
alive = false;
|
||||
game.world.remove_entity(this);
|
||||
game.world.core.characters[player->client_id] = 0;
|
||||
game.create_death(pos, player->client_id);
|
||||
|
||||
// we got to wait 0.5 secs before respawning
|
||||
player->respawn_tick = server_tick()+server_tickspeed()/2;
|
||||
}
|
||||
|
||||
bool CHARACTER::take_damage(vec2 force, int dmg, int from, int weapon)
|
||||
{
|
||||
core.vel += force;
|
||||
|
||||
if(game.controller->is_friendly_fire(player->client_id, from) && !config.sv_teamdamage)
|
||||
return false;
|
||||
|
||||
// player only inflicts half damage on self
|
||||
if(from == player->client_id)
|
||||
dmg = max(1, dmg/2);
|
||||
|
||||
damage_taken++;
|
||||
|
||||
// create healthmod indicator
|
||||
if(server_tick() < damage_taken_tick+25)
|
||||
{
|
||||
// make sure that the damage indicators doesn't group together
|
||||
game.create_damageind(pos, damage_taken*0.25f, dmg);
|
||||
}
|
||||
else
|
||||
{
|
||||
damage_taken = 0;
|
||||
game.create_damageind(pos, 0, dmg);
|
||||
}
|
||||
|
||||
if(dmg)
|
||||
{
|
||||
if(armor)
|
||||
{
|
||||
if(dmg > 1)
|
||||
{
|
||||
health--;
|
||||
dmg--;
|
||||
}
|
||||
|
||||
if(dmg > armor)
|
||||
{
|
||||
dmg -= armor;
|
||||
armor = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
armor -= dmg;
|
||||
dmg = 0;
|
||||
}
|
||||
}
|
||||
|
||||
health -= dmg;
|
||||
}
|
||||
|
||||
damage_taken_tick = server_tick();
|
||||
|
||||
// do damage hit sound
|
||||
if(from >= 0 && from != player->client_id && game.players[from])
|
||||
game.create_sound(game.players[from]->view_pos, SOUND_HIT, cmask_one(from));
|
||||
|
||||
// check for death
|
||||
if(health <= 0)
|
||||
{
|
||||
die(from, weapon);
|
||||
|
||||
// set attacker's face to happy (taunt!)
|
||||
if (from >= 0 && from != player->client_id && game.players[from])
|
||||
{
|
||||
CHARACTER *chr = game.players[from]->get_character();
|
||||
if (chr)
|
||||
{
|
||||
chr->emote_type = EMOTE_HAPPY;
|
||||
chr->emote_stop = server_tick() + server_tickspeed();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dmg > 2)
|
||||
game.create_sound(pos, SOUND_PLAYER_PAIN_LONG);
|
||||
else
|
||||
game.create_sound(pos, SOUND_PLAYER_PAIN_SHORT);
|
||||
|
||||
emote_type = EMOTE_PAIN;
|
||||
emote_stop = server_tick() + 500 * server_tickspeed() / 1000;
|
||||
|
||||
// spawn blood?
|
||||
return true;
|
||||
}
|
||||
|
||||
void CHARACTER::snap(int snapping_client)
|
||||
{
|
||||
if(networkclipped(snapping_client))
|
||||
return;
|
||||
|
||||
NETOBJ_CHARACTER *character = (NETOBJ_CHARACTER *)snap_new_item(NETOBJTYPE_CHARACTER, player->client_id, sizeof(NETOBJ_CHARACTER));
|
||||
|
||||
// write down the core
|
||||
if(game.world.paused)
|
||||
{
|
||||
// no dead reckoning when paused because the client doesn't know
|
||||
// how far to perform the reckoning
|
||||
character->tick = 0;
|
||||
core.write(character);
|
||||
}
|
||||
else
|
||||
{
|
||||
character->tick = reckoning_tick;
|
||||
sendcore.write(character);
|
||||
}
|
||||
|
||||
// set emote
|
||||
if (emote_stop < server_tick())
|
||||
{
|
||||
emote_type = EMOTE_NORMAL;
|
||||
emote_stop = -1;
|
||||
}
|
||||
|
||||
character->emote = emote_type;
|
||||
|
||||
character->ammocount = 0;
|
||||
character->health = 0;
|
||||
character->armor = 0;
|
||||
|
||||
character->weapon = active_weapon;
|
||||
character->attacktick = attack_tick;
|
||||
|
||||
character->direction = input.direction;
|
||||
|
||||
if(player->client_id == snapping_client)
|
||||
{
|
||||
character->health = health;
|
||||
character->armor = armor;
|
||||
if(weapons[active_weapon].ammo > 0)
|
||||
character->ammocount = weapons[active_weapon].ammo;
|
||||
}
|
||||
|
||||
if (character->emote == EMOTE_NORMAL)
|
||||
{
|
||||
if(250 - ((server_tick() - last_action)%(250)) < 5)
|
||||
character->emote = EMOTE_BLINK;
|
||||
}
|
||||
|
||||
character->player_state = player_state;
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifndef GAME_SERVER_ENTITY_CHARACTER_H
|
||||
#define GAME_SERVER_ENTITY_CHARACTER_H
|
||||
|
||||
#include <game/server/entity.hpp>
|
||||
#include <game/generated/gs_data.hpp>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
|
||||
#include <game/gamecore.hpp>
|
||||
|
||||
enum
|
||||
{
|
||||
WEAPON_GAME = -3, // team switching etc
|
||||
WEAPON_SELF = -2, // console kill command
|
||||
WEAPON_WORLD = -1, // death tiles etc
|
||||
};
|
||||
|
||||
class CHARACTER : public ENTITY
|
||||
{
|
||||
MACRO_ALLOC_POOL_ID()
|
||||
public:
|
||||
// player controlling this character
|
||||
class PLAYER *player;
|
||||
|
||||
bool alive;
|
||||
|
||||
// weapon info
|
||||
ENTITY *hitobjects[10];
|
||||
int numobjectshit;
|
||||
struct WEAPONSTAT
|
||||
{
|
||||
int ammoregenstart;
|
||||
int ammo;
|
||||
int ammocost;
|
||||
bool got;
|
||||
} weapons[NUM_WEAPONS];
|
||||
|
||||
int active_weapon;
|
||||
int last_weapon;
|
||||
int queued_weapon;
|
||||
|
||||
int reload_timer;
|
||||
int attack_tick;
|
||||
|
||||
int damage_taken;
|
||||
|
||||
int emote_type;
|
||||
int emote_stop;
|
||||
|
||||
// TODO: clean this up
|
||||
char skin_name[64];
|
||||
int use_custom_color;
|
||||
int color_body;
|
||||
int color_feet;
|
||||
|
||||
int last_action; // last tick that the player took any action ie some input
|
||||
|
||||
// these are non-heldback inputs
|
||||
NETOBJ_PLAYER_INPUT latest_previnput;
|
||||
NETOBJ_PLAYER_INPUT latest_input;
|
||||
|
||||
// input
|
||||
NETOBJ_PLAYER_INPUT previnput;
|
||||
NETOBJ_PLAYER_INPUT input;
|
||||
int num_inputs;
|
||||
int jumped;
|
||||
|
||||
int damage_taken_tick;
|
||||
|
||||
int health;
|
||||
int armor;
|
||||
|
||||
// ninja
|
||||
struct
|
||||
{
|
||||
vec2 activationdir;
|
||||
int activationtick;
|
||||
int currentmovetime;
|
||||
} ninja;
|
||||
|
||||
//
|
||||
//int score;
|
||||
int team;
|
||||
int player_state; // if the client is chatting, accessing a menu or so
|
||||
|
||||
// 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();
|
||||
|
||||
virtual void reset();
|
||||
virtual void destroy();
|
||||
|
||||
bool is_grounded();
|
||||
|
||||
void set_weapon(int w);
|
||||
|
||||
void handle_weaponswitch();
|
||||
void do_weaponswitch();
|
||||
|
||||
int handle_weapons();
|
||||
int handle_ninja();
|
||||
|
||||
void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input);
|
||||
void on_direct_input(NETOBJ_PLAYER_INPUT *new_input);
|
||||
void fire_weapon();
|
||||
|
||||
void die(int killer, int weapon);
|
||||
|
||||
bool take_damage(vec2 force, int dmg, int from, int weapon);
|
||||
|
||||
|
||||
bool spawn(PLAYER *player, vec2 pos, int team);
|
||||
//bool init_tryspawn(int team);
|
||||
bool remove();
|
||||
|
||||
static const int phys_size = 28;
|
||||
|
||||
virtual void tick();
|
||||
virtual void tick_defered();
|
||||
virtual void snap(int snapping_client);
|
||||
|
||||
bool increase_health(int amount);
|
||||
bool increase_armor(int amount);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,115 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
#include <engine/e_server_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/server/gamecontext.hpp>
|
||||
#include "laser.hpp"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// laser
|
||||
//////////////////////////////////////////////////
|
||||
LASER::LASER(vec2 pos, vec2 direction, float start_energy, int owner)
|
||||
: ENTITY(NETOBJTYPE_LASER)
|
||||
{
|
||||
this->pos = pos;
|
||||
this->owner = owner;
|
||||
energy = start_energy;
|
||||
dir = direction;
|
||||
bounces = 0;
|
||||
do_bounce();
|
||||
|
||||
game.world.insert_entity(this);
|
||||
}
|
||||
|
||||
|
||||
bool LASER::hit_character(vec2 from, vec2 to)
|
||||
{
|
||||
vec2 at;
|
||||
CHARACTER *owner_char = game.get_player_char(owner);
|
||||
CHARACTER *hit = game.world.intersect_character(pos, to, 0.0f, at, owner_char);
|
||||
if(!hit)
|
||||
return false;
|
||||
|
||||
this->from = from;
|
||||
pos = at;
|
||||
energy = -1;
|
||||
hit->take_damage(vec2(0,0), tuning.laser_damage, owner, WEAPON_RIFLE);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LASER::do_bounce()
|
||||
{
|
||||
eval_tick = server_tick();
|
||||
|
||||
if(energy < 0)
|
||||
{
|
||||
//dbg_msg("laser", "%d removed", server_tick());
|
||||
game.world.destroy_entity(this);
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 to = pos + dir*energy;
|
||||
vec2 org_to = to;
|
||||
|
||||
if(col_intersect_line(pos, to, 0x0, &to))
|
||||
{
|
||||
if(!hit_character(pos, to))
|
||||
{
|
||||
// intersected
|
||||
from = pos;
|
||||
pos = to;
|
||||
|
||||
vec2 temp_pos = pos;
|
||||
vec2 temp_dir = dir*4.0f;
|
||||
|
||||
move_point(&temp_pos, &temp_dir, 1.0f, 0);
|
||||
pos = temp_pos;
|
||||
dir = normalize(temp_dir);
|
||||
|
||||
energy -= distance(from, pos) + tuning.laser_bounce_cost;
|
||||
bounces++;
|
||||
|
||||
if(bounces > tuning.laser_bounce_num)
|
||||
energy = -1;
|
||||
|
||||
game.create_sound(pos, SOUND_RIFLE_BOUNCE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!hit_character(pos, to))
|
||||
{
|
||||
from = pos;
|
||||
pos = to;
|
||||
energy = -1;
|
||||
}
|
||||
}
|
||||
|
||||
//dbg_msg("laser", "%d done %f %f %f %f", server_tick(), from.x, from.y, pos.x, pos.y);
|
||||
}
|
||||
|
||||
void LASER::reset()
|
||||
{
|
||||
game.world.destroy_entity(this);
|
||||
}
|
||||
|
||||
void LASER::tick()
|
||||
{
|
||||
if(server_tick() > eval_tick+(server_tickspeed()*tuning.laser_bounce_delay)/1000.0f)
|
||||
{
|
||||
do_bounce();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LASER::snap(int snapping_client)
|
||||
{
|
||||
if(networkclipped(snapping_client))
|
||||
return;
|
||||
|
||||
NETOBJ_LASER *obj = (NETOBJ_LASER *)snap_new_item(NETOBJTYPE_LASER, id, sizeof(NETOBJ_LASER));
|
||||
obj->x = (int)pos.x;
|
||||
obj->y = (int)pos.y;
|
||||
obj->from_x = (int)from.x;
|
||||
obj->from_y = (int)from.y;
|
||||
obj->start_tick = eval_tick;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifndef GAME_SERVER_ENTITY_LASER_H
|
||||
#define GAME_SERVER_ENTITY_LASER_H
|
||||
|
||||
#include <game/server/entity.hpp>
|
||||
|
||||
class CHARACTER;
|
||||
|
||||
class LASER : public ENTITY
|
||||
{
|
||||
vec2 from;
|
||||
vec2 dir;
|
||||
float energy;
|
||||
int bounces;
|
||||
int eval_tick;
|
||||
int owner;
|
||||
|
||||
bool hit_character(vec2 from, vec2 to);
|
||||
void do_bounce();
|
||||
|
||||
public:
|
||||
|
||||
LASER(vec2 pos, vec2 direction, float start_energy, int owner);
|
||||
|
||||
virtual void reset();
|
||||
virtual void tick();
|
||||
virtual void snap(int snapping_client);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <engine/e_server_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/server/gamecontext.hpp>
|
||||
#include "pickup.hpp"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// pickup
|
||||
//////////////////////////////////////////////////
|
||||
PICKUP::PICKUP(int _type, int _subtype)
|
||||
: ENTITY(NETOBJTYPE_PICKUP)
|
||||
{
|
||||
type = _type;
|
||||
subtype = _subtype;
|
||||
proximity_radius = phys_size;
|
||||
|
||||
reset();
|
||||
|
||||
// TODO: should this be done here?
|
||||
game.world.insert_entity(this);
|
||||
}
|
||||
|
||||
void PICKUP::reset()
|
||||
{
|
||||
if (data->pickups[type].spawndelay > 0)
|
||||
spawntick = server_tick() + server_tickspeed() * data->pickups[type].spawndelay;
|
||||
else
|
||||
spawntick = -1;
|
||||
}
|
||||
|
||||
void PICKUP::tick()
|
||||
{
|
||||
// wait for respawn
|
||||
if(spawntick > 0)
|
||||
{
|
||||
if(server_tick() > spawntick)
|
||||
{
|
||||
// respawn
|
||||
spawntick = -1;
|
||||
|
||||
if(type == POWERUP_WEAPON)
|
||||
game.create_sound(pos, SOUND_WEAPON_SPAWN);
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if a player intersected us
|
||||
CHARACTER *chr = game.world.closest_character(pos, 20.0f, 0);
|
||||
if(chr && chr->alive)
|
||||
{
|
||||
// player picked us up, is someone was hooking us, let them go
|
||||
int respawntime = -1;
|
||||
switch (type)
|
||||
{
|
||||
case POWERUP_HEALTH:
|
||||
if(chr->increase_health(1))
|
||||
{
|
||||
game.create_sound(pos, SOUND_PICKUP_HEALTH);
|
||||
respawntime = data->pickups[type].respawntime;
|
||||
}
|
||||
break;
|
||||
case POWERUP_ARMOR:
|
||||
if(chr->increase_armor(1))
|
||||
{
|
||||
game.create_sound(pos, SOUND_PICKUP_ARMOR);
|
||||
respawntime = data->pickups[type].respawntime;
|
||||
}
|
||||
break;
|
||||
|
||||
case POWERUP_WEAPON:
|
||||
if(subtype >= 0 && subtype < NUM_WEAPONS)
|
||||
{
|
||||
if(chr->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !chr->weapons[subtype].got)
|
||||
{
|
||||
chr->weapons[subtype].got = true;
|
||||
chr->weapons[subtype].ammo = min(data->weapons.id[subtype].maxammo, chr->weapons[subtype].ammo + 10);
|
||||
respawntime = data->pickups[type].respawntime;
|
||||
|
||||
// TODO: data compiler should take care of stuff like this
|
||||
if(subtype == WEAPON_GRENADE)
|
||||
game.create_sound(pos, SOUND_PICKUP_GRENADE);
|
||||
else if(subtype == WEAPON_SHOTGUN)
|
||||
game.create_sound(pos, SOUND_PICKUP_SHOTGUN);
|
||||
else if(subtype == WEAPON_RIFLE)
|
||||
game.create_sound(pos, SOUND_PICKUP_SHOTGUN);
|
||||
|
||||
if(chr->player)
|
||||
game.send_weapon_pickup(chr->player->client_id, subtype);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case POWERUP_NINJA:
|
||||
{
|
||||
// activate ninja on target player
|
||||
chr->ninja.activationtick = server_tick();
|
||||
chr->weapons[WEAPON_NINJA].got = true;
|
||||
chr->weapons[WEAPON_NINJA].ammo = -1;
|
||||
chr->last_weapon = chr->active_weapon;
|
||||
chr->active_weapon = WEAPON_NINJA;
|
||||
respawntime = data->pickups[type].respawntime;
|
||||
game.create_sound(pos, SOUND_PICKUP_NINJA);
|
||||
|
||||
// loop through all players, setting their emotes
|
||||
ENTITY *ents[64];
|
||||
int num = game.world.find_entities(vec2(0, 0), 1000000, ents, 64, NETOBJTYPE_CHARACTER);
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
CHARACTER *c = (CHARACTER *)ents[i];
|
||||
if (c != chr)
|
||||
{
|
||||
c->emote_type = EMOTE_SURPRISE;
|
||||
c->emote_stop = server_tick() + server_tickspeed();
|
||||
}
|
||||
}
|
||||
|
||||
chr->emote_type = EMOTE_ANGRY;
|
||||
chr->emote_stop = server_tick() + 1200 * server_tickspeed() / 1000;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
if(respawntime >= 0)
|
||||
{
|
||||
dbg_msg("game", "pickup player='%d:%s' item=%d/%d",
|
||||
chr->player->client_id, server_clientname(chr->player->client_id), type, subtype);
|
||||
spawntick = server_tick() + server_tickspeed() * respawntime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PICKUP::snap(int snapping_client)
|
||||
{
|
||||
if(spawntick != -1)
|
||||
return;
|
||||
|
||||
NETOBJ_PICKUP *up = (NETOBJ_PICKUP *)snap_new_item(NETOBJTYPE_PICKUP, id, sizeof(NETOBJ_PICKUP));
|
||||
up->x = (int)pos.x;
|
||||
up->y = (int)pos.y;
|
||||
up->type = type; // TODO: two diffrent types? what gives?
|
||||
up->subtype = subtype;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifndef GAME_SERVER_ENTITY_PICKUP_H
|
||||
#define GAME_SERVER_ENTITY_PICKUP_H
|
||||
|
||||
#include <game/server/entity.hpp>
|
||||
|
||||
// TODO: move to seperate file
|
||||
class PICKUP : public ENTITY
|
||||
{
|
||||
public:
|
||||
static const int phys_size = 14;
|
||||
|
||||
int type;
|
||||
int subtype; // weapon type for instance?
|
||||
int spawntick;
|
||||
PICKUP(int _type, int _subtype = 0);
|
||||
|
||||
virtual void reset();
|
||||
virtual void tick();
|
||||
virtual void snap(int snapping_client);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <engine/e_server_interface.h>
|
||||
#include <game/generated/g_protocol.hpp>
|
||||
#include <game/server/gamecontext.hpp>
|
||||
#include "projectile.hpp"
|
||||
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// projectile
|
||||
//////////////////////////////////////////////////
|
||||
PROJECTILE::PROJECTILE(int type, int owner, vec2 pos, vec2 dir, int span,
|
||||
int damage, int flags, float force, int sound_impact, int weapon)
|
||||
: ENTITY(NETOBJTYPE_PROJECTILE)
|
||||
{
|
||||
this->type = type;
|
||||
this->pos = pos;
|
||||
this->direction = dir;
|
||||
this->lifespan = span;
|
||||
this->owner = owner;
|
||||
this->flags = flags;
|
||||
this->force = force;
|
||||
this->damage = damage;
|
||||
this->sound_impact = sound_impact;
|
||||
this->weapon = weapon;
|
||||
this->bounce = 0;
|
||||
this->start_tick = server_tick();
|
||||
game.world.insert_entity(this);
|
||||
}
|
||||
|
||||
void PROJECTILE::reset()
|
||||
{
|
||||
game.world.destroy_entity(this);
|
||||
}
|
||||
|
||||
vec2 PROJECTILE::get_pos(float time)
|
||||
{
|
||||
float curvature = 0;
|
||||
float speed = 0;
|
||||
if(type == WEAPON_GRENADE)
|
||||
{
|
||||
curvature = tuning.grenade_curvature;
|
||||
speed = tuning.grenade_speed;
|
||||
}
|
||||
else if(type == WEAPON_SHOTGUN)
|
||||
{
|
||||
curvature = tuning.shotgun_curvature;
|
||||
speed = tuning.shotgun_speed;
|
||||
}
|
||||
else if(type == WEAPON_GUN)
|
||||
{
|
||||
curvature = tuning.gun_curvature;
|
||||
speed = tuning.gun_speed;
|
||||
}
|
||||
|
||||
return calc_pos(pos, direction, curvature, speed, time);
|
||||
}
|
||||
|
||||
|
||||
void PROJECTILE::tick()
|
||||
{
|
||||
|
||||
float pt = (server_tick()-start_tick-1)/(float)server_tickspeed();
|
||||
float ct = (server_tick()-start_tick)/(float)server_tickspeed();
|
||||
vec2 prevpos = get_pos(pt);
|
||||
vec2 curpos = get_pos(ct);
|
||||
|
||||
lifespan--;
|
||||
|
||||
int collide = col_intersect_line(prevpos, curpos, &curpos, 0);
|
||||
//int collide = col_check_point((int)curpos.x, (int)curpos.y);
|
||||
CHARACTER *ownerchar = game.get_player_char(owner);
|
||||
CHARACTER *targetchr = game.world.intersect_character(prevpos, curpos, 6.0f, curpos, ownerchar);
|
||||
if(targetchr || collide || lifespan < 0)
|
||||
{
|
||||
if(lifespan >= 0 || weapon == WEAPON_GRENADE)
|
||||
game.create_sound(curpos, sound_impact);
|
||||
|
||||
if(flags & PROJECTILE_FLAGS_EXPLODE)
|
||||
game.create_explosion(curpos, owner, weapon, false);
|
||||
else if(targetchr)
|
||||
targetchr->take_damage(direction * max(0.001f, force), damage, owner, weapon);
|
||||
|
||||
game.world.destroy_entity(this);
|
||||
}
|
||||
}
|
||||
|
||||
void PROJECTILE::fill_info(NETOBJ_PROJECTILE *proj)
|
||||
{
|
||||
proj->x = (int)pos.x;
|
||||
proj->y = (int)pos.y;
|
||||
proj->vx = (int)(direction.x*100.0f);
|
||||
proj->vy = (int)(direction.y*100.0f);
|
||||
proj->start_tick = start_tick;
|
||||
proj->type = type;
|
||||
}
|
||||
|
||||
void PROJECTILE::snap(int snapping_client)
|
||||
{
|
||||
float ct = (server_tick()-start_tick)/(float)server_tickspeed();
|
||||
|
||||
if(networkclipped(snapping_client, get_pos(ct)))
|
||||
return;
|
||||
|
||||
NETOBJ_PROJECTILE *proj = (NETOBJ_PROJECTILE *)snap_new_item(NETOBJTYPE_PROJECTILE, id, sizeof(NETOBJ_PROJECTILE));
|
||||
fill_info(proj);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
|
||||
|
||||
#ifndef GAME_SERVER_ENTITY_PROJECTILE_H
|
||||
#define GAME_SERVER_ENTITY_PROJECTILE_H
|
||||
|
||||
class PROJECTILE : public ENTITY
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
PROJECTILE_FLAGS_EXPLODE = 1 << 0,
|
||||
};
|
||||
|
||||
vec2 direction;
|
||||
int lifespan;
|
||||
int owner;
|
||||
int type;
|
||||
int flags;
|
||||
int damage;
|
||||
int sound_impact;
|
||||
int weapon;
|
||||
int bounce;
|
||||
float force;
|
||||
int start_tick;
|
||||
|
||||
PROJECTILE(int type, int owner, vec2 pos, vec2 vel, int span,
|
||||
int damage, int flags, float force, int sound_impact, int weapon);
|
||||
|
||||
vec2 get_pos(float time);
|
||||
void fill_info(NETOBJ_PROJECTILE *proj);
|
||||
|
||||
virtual void reset();
|
||||
virtual void tick();
|
||||
virtual void snap(int snapping_client);
|
||||
};
|
||||
|
||||
#endif
|
||||
49
project/jni/application/teeworlds/src/game/server/entity.cpp
Normal file
49
project/jni/application/teeworlds/src/game/server/entity.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
#include <engine/e_server_interface.h>
|
||||
#include "entity.hpp"
|
||||
#include "gamecontext.hpp"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Entity
|
||||
//////////////////////////////////////////////////
|
||||
ENTITY::ENTITY(int objtype)
|
||||
{
|
||||
this->objtype = objtype;
|
||||
pos = vec2(0,0);
|
||||
proximity_radius = 0;
|
||||
|
||||
marked_for_destroy = false;
|
||||
id = snap_new_id();
|
||||
|
||||
next_entity = 0;
|
||||
prev_entity = 0;
|
||||
prev_type_entity = 0;
|
||||
next_type_entity = 0;
|
||||
}
|
||||
|
||||
ENTITY::~ENTITY()
|
||||
{
|
||||
game.world.remove_entity(this);
|
||||
snap_free_id(id);
|
||||
}
|
||||
|
||||
int ENTITY::networkclipped(int snapping_client)
|
||||
{
|
||||
return networkclipped(snapping_client, pos);
|
||||
}
|
||||
|
||||
int ENTITY::networkclipped(int snapping_client, vec2 check_pos)
|
||||
{
|
||||
if(snapping_client == -1)
|
||||
return 0;
|
||||
|
||||
float dx = game.players[snapping_client]->view_pos.x-check_pos.x;
|
||||
float dy = game.players[snapping_client]->view_pos.y-check_pos.y;
|
||||
|
||||
if(fabs(dx) > 1000.0f || fabs(dy) > 800.0f)
|
||||
return 1;
|
||||
|
||||
if(distance(game.players[snapping_client]->view_pos, check_pos) > 1100.0f)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
145
project/jni/application/teeworlds/src/game/server/entity.hpp
Normal file
145
project/jni/application/teeworlds/src/game/server/entity.hpp
Normal file
@@ -0,0 +1,145 @@
|
||||
#ifndef GAME_SERVER_ENTITY_H
|
||||
#define GAME_SERVER_ENTITY_H
|
||||
|
||||
#include <new>
|
||||
#include <base/vmath.hpp>
|
||||
|
||||
#define MACRO_ALLOC_HEAP() \
|
||||
public: \
|
||||
void *operator new(size_t size) \
|
||||
{ \
|
||||
void *p = mem_alloc(size, 1); \
|
||||
/*dbg_msg("", "++ %p %d", p, size);*/ \
|
||||
mem_zero(p, size); \
|
||||
return p; \
|
||||
} \
|
||||
void operator delete(void *p) \
|
||||
{ \
|
||||
/*dbg_msg("", "-- %p", p);*/ \
|
||||
mem_free(p); \
|
||||
} \
|
||||
private:
|
||||
|
||||
#define MACRO_ALLOC_POOL_ID() \
|
||||
public: \
|
||||
void *operator new(size_t size, int id); \
|
||||
void operator delete(void *p); \
|
||||
private:
|
||||
|
||||
#define MACRO_ALLOC_POOL_ID_IMPL(POOLTYPE, poolsize) \
|
||||
static char pool_data_##POOLTYPE[poolsize][sizeof(POOLTYPE)] = {{0}}; \
|
||||
static int pool_used_##POOLTYPE[poolsize] = {0}; \
|
||||
void *POOLTYPE::operator new(size_t size, int id) \
|
||||
{ \
|
||||
dbg_assert(sizeof(POOLTYPE) == size, "size error"); \
|
||||
dbg_assert(!pool_used_##POOLTYPE[id], "already used"); \
|
||||
/*dbg_msg("pool", "++ %s %d", #POOLTYPE, id);*/ \
|
||||
pool_used_##POOLTYPE[id] = 1; \
|
||||
mem_zero(pool_data_##POOLTYPE[id], size); \
|
||||
return pool_data_##POOLTYPE[id]; \
|
||||
} \
|
||||
void POOLTYPE::operator delete(void *p) \
|
||||
{ \
|
||||
int id = (POOLTYPE*)p - (POOLTYPE*)pool_data_##POOLTYPE; \
|
||||
dbg_assert(pool_used_##POOLTYPE[id], "not used"); \
|
||||
/*dbg_msg("pool", "-- %s %d", #POOLTYPE, id);*/ \
|
||||
pool_used_##POOLTYPE[id] = 0; \
|
||||
mem_zero(pool_data_##POOLTYPE[id], sizeof(POOLTYPE)); \
|
||||
}
|
||||
|
||||
/*
|
||||
Class: Entity
|
||||
Basic entity class.
|
||||
*/
|
||||
class ENTITY
|
||||
{
|
||||
MACRO_ALLOC_HEAP()
|
||||
private:
|
||||
friend class GAMEWORLD; // thy these?
|
||||
ENTITY *prev_entity;
|
||||
ENTITY *next_entity;
|
||||
|
||||
ENTITY *prev_type_entity;
|
||||
ENTITY *next_type_entity;
|
||||
protected:
|
||||
bool marked_for_destroy;
|
||||
int id;
|
||||
int objtype;
|
||||
public:
|
||||
ENTITY(int objtype);
|
||||
virtual ~ENTITY();
|
||||
|
||||
ENTITY *typenext() { return next_type_entity; }
|
||||
ENTITY *typeprev() { return prev_type_entity; }
|
||||
|
||||
/*
|
||||
Function: destroy
|
||||
Destorys the entity.
|
||||
*/
|
||||
virtual void destroy() { delete this; }
|
||||
|
||||
/*
|
||||
Function: reset
|
||||
Called when the game resets the map. Puts the entity
|
||||
back to it's starting state or perhaps destroys it.
|
||||
*/
|
||||
virtual void reset() {}
|
||||
|
||||
/*
|
||||
Function: tick
|
||||
Called progress the entity to the next tick. Updates
|
||||
and moves the entity to it's new state and position.
|
||||
*/
|
||||
virtual void tick() {}
|
||||
|
||||
/*
|
||||
Function: tick_defered
|
||||
Called after all entities tick() function has been called.
|
||||
*/
|
||||
virtual void tick_defered() {}
|
||||
|
||||
/*
|
||||
Function: snap
|
||||
Called when a new snapshot is being generated for a specific
|
||||
client.
|
||||
|
||||
Arguments:
|
||||
snapping_client - ID of the client which snapshot is
|
||||
being generated. Could be -1 to create a complete
|
||||
snapshot of everything in the game for demo
|
||||
recording.
|
||||
*/
|
||||
virtual void snap(int snapping_client) {}
|
||||
|
||||
/*
|
||||
Function: networkclipped(int snapping_client)
|
||||
Performs a series of test to see if a client can see the
|
||||
entity.
|
||||
|
||||
Arguments:
|
||||
snapping_client - ID of the client which snapshot is
|
||||
being generated. Could be -1 to create a complete
|
||||
snapshot of everything in the game for demo
|
||||
recording.
|
||||
|
||||
Returns:
|
||||
Non-zero if the entity doesn't have to be in the snapshot.
|
||||
*/
|
||||
int networkclipped(int snapping_client);
|
||||
int networkclipped(int snapping_client, vec2 check_pos);
|
||||
|
||||
|
||||
/*
|
||||
Variable: proximity_radius
|
||||
Contains the physical size of the entity.
|
||||
*/
|
||||
float proximity_radius;
|
||||
|
||||
/*
|
||||
Variable: pos
|
||||
Contains the current posititon of the entity.
|
||||
*/
|
||||
vec2 pos;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "eventhandler.hpp"
|
||||
#include "gamecontext.hpp"
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Event handler
|
||||
//////////////////////////////////////////////////
|
||||
EVENTHANDLER::EVENTHANDLER()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void *EVENTHANDLER::create(int type, int size, int mask)
|
||||
{
|
||||
if(num_events == MAX_EVENTS)
|
||||
return 0;
|
||||
if(current_offset+size >= MAX_DATASIZE)
|
||||
return 0;
|
||||
|
||||
void *p = &data[current_offset];
|
||||
offsets[num_events] = current_offset;
|
||||
types[num_events] = type;
|
||||
sizes[num_events] = size;
|
||||
client_masks[num_events] = mask;
|
||||
current_offset += size;
|
||||
num_events++;
|
||||
return p;
|
||||
}
|
||||
|
||||
void EVENTHANDLER::clear()
|
||||
{
|
||||
num_events = 0;
|
||||
current_offset = 0;
|
||||
}
|
||||
|
||||
void EVENTHANDLER::snap(int snapping_client)
|
||||
{
|
||||
for(int i = 0; i < num_events; i++)
|
||||
{
|
||||
if(snapping_client == -1 || cmask_is_set(client_masks[i], snapping_client))
|
||||
{
|
||||
NETEVENT_COMMON *ev = (NETEVENT_COMMON *)&data[offsets[i]];
|
||||
if(snapping_client == -1 || distance(game.players[snapping_client]->view_pos, vec2(ev->x, ev->y)) < 1500.0f)
|
||||
{
|
||||
void *d = snap_new_item(types[i], i, sizes[i]);
|
||||
mem_copy(d, &data[offsets[i]], sizes[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user