From 906ece7894927983b8ac69e37dd3cb82cfe7aad1 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Sat, 12 Jan 2008 17:09:00 +0000 Subject: continued the cleanup --- src/game/client/gc_anim.h | 14 + src/game/client/gc_client.cpp | 1327 +---------------- src/game/client/gc_client.h | 93 ++ src/game/client/gc_hooks.cpp | 500 +++++++ src/game/client/gc_mapres_tilemap.cpp | 6 + src/game/client/gc_mapres_tilemap.h | 6 - src/game/client/gc_menu.cpp | 14 +- src/game/client/gc_menu.h | 4 +- src/game/client/gc_render.cpp | 237 ++++ src/game/client/gc_render.h | 60 +- src/game/client/gc_render_map.cpp | 167 +++ src/game/client/gc_render_obj.cpp | 478 +++++++ src/game/editor/ed_editor.cpp | 2441 ++++++++++++++++++++++++++++++++ src/game/editor/ed_editor.hpp | 474 +++++++ src/game/editor/ed_layer_game.cpp | 2 +- src/game/editor/ed_layer_quads.cpp | 86 +- src/game/editor/ed_layer_tiles.cpp | 82 +- src/game/editor/editor.cpp | 2501 --------------------------------- src/game/editor/editor.hpp | 569 -------- src/game/g_game.h | 5 + src/game/g_mapitems.h | 138 ++ src/game/g_mapres_col.h | 6 + src/game/g_math.h | 5 + src/game/g_protocol.h | 5 + 24 files changed, 4668 insertions(+), 4552 deletions(-) create mode 100644 src/game/client/gc_anim.h create mode 100644 src/game/client/gc_client.h create mode 100644 src/game/client/gc_hooks.cpp create mode 100644 src/game/client/gc_render.cpp create mode 100644 src/game/client/gc_render_map.cpp create mode 100644 src/game/client/gc_render_obj.cpp create mode 100644 src/game/editor/ed_editor.cpp create mode 100644 src/game/editor/ed_editor.hpp delete mode 100644 src/game/editor/editor.cpp delete mode 100644 src/game/editor/editor.hpp create mode 100644 src/game/g_mapitems.h (limited to 'src') diff --git a/src/game/client/gc_anim.h b/src/game/client/gc_anim.h new file mode 100644 index 00000000..115f7353 --- /dev/null +++ b/src/game/client/gc_anim.h @@ -0,0 +1,14 @@ + +struct animstate +{ + keyframe body; + keyframe back_foot; + keyframe front_foot; + keyframe attach; +}; + +void anim_seq_eval(sequence *seq, float time, keyframe *frame); +void anim_eval(animation *anim, float time, animstate *state); +void anim_add_keyframe(keyframe *seq, keyframe *added, float amount); +void anim_add(animstate *state, animstate *added, float amount); +void anim_eval_add(animstate *state, animation *anim, float time, float amount); diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp index 295b729f..556fabee 100644 --- a/src/game/client/gc_client.cpp +++ b/src/game/client/gc_client.cpp @@ -17,126 +17,52 @@ extern "C" { #include "../generated/gc_data.h" #include "gc_menu.h" #include "gc_skin.h" -#include "gc_render.h" #include "gc_ui.h" +#include "gc_client.h" +#include "gc_render.h" +#include "gc_anim.h" -// sound channels -enum -{ - CHN_GUI=0, - CHN_MUSIC, - CHN_WORLD, - CHN_GLOBAL, -}; - -data_container *data = 0x0; - -static player_input input_data = {0}; -static int input_target_lock = 0; - +struct data_container *data = 0; static int64 debug_firedelay = 0; -extern void modmenu_render(); -extern void menu_init(); - -enum -{ - CHATMODE_NONE=0, - CHATMODE_ALL, - CHATMODE_TEAM, - CHATMODE_CONSOLE, - CHATMODE_REMOTECONSOLE, -}; +player_input input_data = {0}; +int input_target_lock = 0; -static int chat_mode = CHATMODE_NONE; +int chat_mode = CHATMODE_NONE; bool menu_active = false; bool menu_game_active = false; -static int snapshot_count = 0; static bool emoticon_selector_active = false; -static vec2 mouse_pos; -static vec2 local_character_pos; -static vec2 local_target_pos; -static const obj_player_character *local_character = 0; -static const obj_player_character *local_prev_character = 0; + +vec2 mouse_pos; +vec2 local_character_pos; +vec2 local_target_pos; +const obj_player_character *local_character = 0; +const obj_player_character *local_prev_character = 0; const obj_player_info *local_info = 0; -static const obj_flag *flags[2] = {0,0}; +const obj_flag *flags[2] = {0,0}; const obj_game *gameobj = 0; -static int picked_up_weapon = -1; - -static struct client_data -{ - char name[64]; - char skin_name[64]; - int skin_id; - int skin_color; - int team; - int emoticon; - int emoticon_start; - player_core predicted; - - tee_render_info skin_info; // this is what the server reports - tee_render_info render_info; // this is what we use - - void update_render_info() - { - render_info = skin_info; - - // force team colors - if(gameobj && gameobj->gametype != GAMETYPE_DM) - { - const int team_colors[2] = {65387, 10223467}; - if(team >= 0 || team <= 1) - { - render_info.texture = skin_get(skin_id)->color_texture; - render_info.color_body = skin_get_color(team_colors[team]); - render_info.color_feet = skin_get_color(team_colors[team]); - } - } - } -} client_datas[MAX_CLIENTS]; +int picked_up_weapon = -1; -class client_effects +client_data client_datas[MAX_CLIENTS]; +void client_data::update_render_info() { -public: - float zoom; - float currentzoom; - float stage; - int lastzoomin; - int lastincrease; - - client_effects() - { - currentzoom = zoom = 3.0f; - stage = 0.0f; - } + render_info = skin_info; - float getorgzoom() { return zoom; } - - float getzoom(int tick, float intratick, const obj_player_character *player) + // force team colors + if(gameobj && gameobj->gametype != GAMETYPE_DM) { - float currentstage = ((float)player->weaponstage) * 0.1f; - if (currentstage < stage) - { - if ((tick - lastincrease) > (client_tickspeed() / 2)) - stage = currentstage; - } - else + const int team_colors[2] = {65387, 10223467}; + if(team >= 0 || team <= 1) { - lastincrease = tick; - stage = currentstage; + render_info.texture = skin_get(skin_id)->color_texture; + render_info.color_body = skin_get_color(team_colors[team]); + render_info.color_feet = skin_get_color(team_colors[team]); } + } +} - float targetzoom = 3.0f + stage; - currentzoom = LERP(currentzoom, targetzoom, 0.1); - return currentzoom; - } -}; - -client_effects cl_effects; - -inline float frandom() { return rand()/(float)(RAND_MAX); } void snd_play_random(int chn, int setid, float vol, vec2 pos) { @@ -160,96 +86,6 @@ void snd_play_random(int chn, int setid, float vol, vec2 pos) set->last = id; } -// sound volume tweak -static const float stereo_separation = 0.001f; -static const float stereo_separation_deadzone = 200.0f; -static const float volume_distance_falloff = 200.0f; -static const float volume_distance_deadzone = 320.0f; -static const float volume_gun = 0.5f; -static const float volume_tee = 0.5f; -static const float volume_hit = 0.5f; -static const float volume_music = 0.8f; - -void sound_vol_pan(const vec2& p, float *vol, float *pan) -{ - vec2 player_to_ev = p - local_character_pos; - *pan = 0.0f; - *vol = 1.0f; - - if(fabs(player_to_ev.x) > stereo_separation_deadzone) - { - *pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone); - if(*pan < -1.0f) *pan = -1.0f; - if(*pan > 1.0f) *pan = 1.0f; - } - - float len = length(player_to_ev); - if(len > volume_distance_deadzone) - { - *vol = volume_distance_falloff / (len - volume_distance_deadzone); - - if(*vol < 0.0f) *vol = 0.0f; - if(*vol > 1.0f) *vol = 1.0f; - } -} - -enum -{ - SPRITE_FLAG_FLIP_Y=1, - SPRITE_FLAG_FLIP_X=2, -}; - -static float sprite_w_scale; -static float sprite_h_scale; - -static void select_sprite(sprite *spr, int flags=0, int sx=0, int sy=0) -{ - 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=0, int sx=0, int sy=0) -{ - 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); -} - class damage_indicators { public: @@ -477,8 +313,14 @@ public: } } }; + static projectile_particles proj_particles; +void reset_projectile_particles() // TODO: remove +{ + proj_particles.reset(); +} + static char chat_input[512]; static unsigned chat_input_len; static const int chat_max_lines = 10; @@ -534,22 +376,11 @@ void chat_add_line(int client_id, int team, const char *line) } } -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]; -static int killmsg_current = 0; +int killmsg_current = 0; -extern unsigned char internal_data[]; - -void create_air_jump_effect(vec2 pos) +void effect_air_jump(vec2 pos) { const int count = 12; for(int i = 0; i <= count; i++) @@ -560,83 +391,15 @@ void create_air_jump_effect(vec2 pos) } } - -extern void draw_round_rect(float x, float y, float w, float h, float r); extern int render_popup(const char *caption, const char *text, const char *button_text); -void render_loading(float percent); - -extern "C" void modc_init() -{ - static FONT_SET default_font; - - 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 usage: %d", gfx_memory_usage()-before); - - gfx_text_set_default_font(&default_font); - - menu_init(); - - // setup sound channels - snd_set_channel(CHN_GUI, 1.0f, 0.0f); - snd_set_channel(CHN_MUSIC, 1.0f, 0.0f); - snd_set_channel(CHN_WORLD, 0.9f, 1.0f); - snd_set_channel(CHN_GLOBAL, 1.0f, 0.0f); - - // load the data container - data = load_data_from_memory(internal_data); - - // TODO: should be removed - snd_set_listener_pos(0.0f, 0.0f); - - float total = data->num_sounds+data->num_images; - float current = 0; - - // load textures - for(int i = 0; i < data->num_images; i++) - { - render_loading(current/total); - data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO); - current++; - } - - // load sounds - for(int s = 0; s < data->num_sounds; s++) - { - render_loading(current/total); - for(int i = 0; i < data->sounds[s].num_sounds; i++) - { - int id; - //if (strcmp(data->sounds[s].sounds[i].filename + strlen(data->sounds[s].sounds[i].filename) - 3, ".wv") == 0) - id = snd_load_wv(data->sounds[s].sounds[i].filename); - //else - // id = snd_load_wav(data->sounds[s].sounds[i].filename); - data->sounds[s].sounds[i].id = id; - } - - current++; - } - - skin_init(); -} - -extern "C" void modc_entergame() -{ -} - -extern "C" void modc_shutdown() -{ - // shutdown the menu -} - -static void process_events(int s) +void process_events(int snaptype) { - int num = snap_num_items(s); + int num = snap_num_items(snaptype); for(int index = 0; index < num; index++) { SNAP_ITEM item; - const void *data = snap_get_item(s, index, &item); + const void *data = snap_get_item(snaptype, index, &item); if(item.type == EVENT_DAMAGEINDICATION) { @@ -646,7 +409,7 @@ static void process_events(int s) else if(item.type == EVENT_AIR_JUMP) { ev_common *ev = (ev_common *)data; - create_air_jump_effect(vec2(ev->x, ev->y)); + effect_air_jump(vec2(ev->x, ev->y)); } else if(item.type == EVENT_EXPLOSION) { @@ -762,139 +525,7 @@ static void process_events(int s) } } -static player_core predicted_prev_player; -static player_core predicted_player; -static int predicted_tick = 0; -static int last_new_predicted_tick = -1; - -extern "C" void modc_predict() -{ - player_core before_prev_player = predicted_prev_player; - player_core before_player = predicted_player; - - - // repredict player - world_core world; - int local_cid = -1; - - // search for players - for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) - { - SNAP_ITEM item; - const void *data = snap_get_item(SNAP_CURRENT, i, &item); - int client_id = item.id; - - if(item.type == OBJTYPE_PLAYER_CHARACTER) - { - const obj_player_character *character = (const obj_player_character *)data; - client_datas[client_id].predicted.world = &world; - world.players[client_id] = &client_datas[client_id].predicted; - - client_datas[client_id].predicted.read(character); - } - else if(item.type == OBJTYPE_PLAYER_INFO) - { - const obj_player_info *info = (const obj_player_info *)data; - if(info->local) - local_cid = client_id; - } - } - - // predict - for(int tick = client_tick()+1; tick <= client_predtick(); tick++) - { - // fetch the local - if(tick == client_predtick() && world.players[local_cid]) - predicted_prev_player = *world.players[local_cid]; - - // first calculate where everyone should move - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(!world.players[c]) - continue; - - mem_zero(&world.players[c]->input, sizeof(world.players[c]->input)); - if(local_cid == c) - { - // apply player input - int *input = client_get_input(tick); - if(input) - world.players[c]->input = *((player_input*)input); - } - - world.players[c]->tick(); - } - - // move all players and quantize their data - for(int c = 0; c < MAX_CLIENTS; c++) - { - if(!world.players[c]) - continue; - - - // TODO: this should be moved into the g_game - // but not done to preserve the nethash - if(length(world.players[c]->vel) > 150.0f) - world.players[c]->vel = normalize(world.players[c]->vel) * 150.0f; - - world.players[c]->move(); - world.players[c]->quantize(); - } - - if(tick > last_new_predicted_tick) - { - last_new_predicted_tick = tick; - - if(local_cid != -1 && world.players[local_cid]) - { - vec2 pos = world.players[local_cid]->pos; - int events = world.players[local_cid]->triggered_events; - if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - if(events&COREEVENT_AIR_JUMP) - { - create_air_jump_effect(pos); - snd_play_random(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) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos); - //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); - } - - - /* - dbg_msg("predict", "%d %d %d", tick, - (int)world.players[c]->pos.x, (int)world.players[c]->pos.y, - (int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/ - } - - if(tick == client_predtick() && world.players[local_cid]) - predicted_player = *world.players[local_cid]; - } - - if(config.debug && predicted_tick == client_predtick()) - { - if(predicted_player.pos.x != before_player.pos.x || - predicted_player.pos.y != before_player.pos.y) - { - dbg_msg("client", "prediction error, (%d %d) (%d %d)", - (int)before_player.pos.x, (int)before_player.pos.y, - (int)predicted_player.pos.x, (int)predicted_player.pos.y); - } - - if(predicted_prev_player.pos.x != before_prev_player.pos.x || - predicted_prev_player.pos.y != before_prev_player.pos.y) - { - dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", - (int)before_prev_player.pos.x, (int)before_prev_player.pos.y, - (int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y); - } - } - - predicted_tick = client_predtick(); -} - -static void clear_object_pointers() +void clear_object_pointers() { // clear out the invalid pointers local_character = 0; @@ -905,69 +536,6 @@ static void clear_object_pointers() gameobj = 0; } -extern "C" void modc_newsnapshot() -{ - snapshot_count++; - process_events(SNAP_CURRENT); - - if(config.dbg_stress) - { - if((client_tick()%250) == 0) - { - msg_pack_start(MSG_SAY, MSGFLAG_VITAL); - msg_pack_int(-1); - msg_pack_string("galenskap!!!!", 512); - msg_pack_end(); - client_send_msg(); - } - } - - clear_object_pointers(); - - // setup world view - { - // 1. fetch local player - // 2. set him to the center - 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 == OBJTYPE_PLAYER_INFO) - { - const obj_player_info *info = (const obj_player_info *)data; - - client_datas[info->clientid].team = info->team; - - if(info->local) - { - local_info = info; - const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id); - if(data) - { - local_character = (const obj_player_character *)data; - local_character_pos = vec2(local_character->x, local_character->y); - - const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id); - if(p) - local_prev_character = (obj_player_character *)p; - } - } - } - else if(item.type == OBJTYPE_GAME) - gameobj = (obj_game *)data; - else if(item.type == OBJTYPE_FLAG) - { - flags[item.id%2] = (const obj_flag *)data; - } - } - } - - for(int i = 0; i < MAX_CLIENTS; i++) - client_datas[i].update_render_info(); -} - void send_info(bool start) { if(start) @@ -991,121 +559,6 @@ void send_emoticon(int emoticon) client_send_msg(); } -static void render_projectile(const obj_projectile *current, int itemid) -{ - if(debug_firedelay) - { - debug_firedelay = time_get()-debug_firedelay; - dbg_msg("game", "firedelay=%.2f ms", debug_firedelay/(float)time_freq()*1000.0f); - debug_firedelay = 0; - } - - gfx_texture_set(data->images[IMAGE_GAME].id); - gfx_quads_begin(); - - // get positions - float gravity = -400; - if(current->type != WEAPON_ROCKET) - gravity = -100; - if(current->type == WEAPON_BOMB) - gravity = 0; - - float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED; - vec2 startpos(current->x, current->y); - vec2 startvel(current->vx, current->vy); - vec2 pos = calc_pos(startpos, startvel, gravity, ct); - vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f); - - select_sprite(data->weapons[current->type%data->num_weapons].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 - proj_particles.addparticle(current->type, itemid, pos, vel); - - if(length(vel) > 0.00001f) - gfx_quads_setrotation(get_angle(vel)); - else - gfx_quads_setrotation(0); - - // TODO: do this, but nice - //temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f); - - gfx_quads_draw(pos.x, pos.y, 32, 32); - gfx_quads_setrotation(0); - gfx_quads_end(); -} - -static void render_powerup(const obj_powerup *prev, const obj_powerup *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[current->subtype%data->num_weapons].sprite_body); - size = data->weapons[current->subtype%data->num_weapons].visual_size; - } - else - { - const int c[] = { - SPRITE_POWERUP_HEALTH, - SPRITE_POWERUP_ARMOR, - SPRITE_POWERUP_WEAPON, - SPRITE_POWERUP_NINJA, - SPRITE_POWERUP_TIMEFIELD - }; - select_sprite(c[current->type]); - - if(c[current->type] == SPRITE_POWERUP_NINJA) - { - proj_particles.addparticle(0, 0, - pos+vec2((frandom()-0.5f)*80.0f, (frandom()-0.5f)*20.0f), - vec2((frandom()-0.5f)*10.0f, (frandom()-0.5f)*10.0f)); - 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(); -} - -static void render_flag(const obj_flag *prev, const obj_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()); - - if(local_info && current->carried_by == local_info->clientid) - pos = local_character_pos; - - //gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1); - //draw_sprite(pos.x, pos.y, size); - gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); - gfx_quads_end(); -} - void anim_seq_eval(sequence *seq, float time, keyframe *frame) { if(seq->num_frames == 0) @@ -1178,127 +631,6 @@ void anim_eval_add(animstate *state, animation *anim, float time, float amount) anim_add(state, &add, amount); } -static void 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(); -} - -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, info->color_body.a); - 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 - gfx_setcolor(info->color_feet.r, info->color_feet.g, info->color_feet.b, info->color_feet.a); - select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, 0); - - keyframe *foot = f ? &anim->front_foot : &anim->back_foot; - - float w = basesize; - float h = basesize/2; - - gfx_quads_setrotation(foot->angle*pi*2); - gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h); - } - } - - gfx_quads_end(); -} - void draw_circle(float x, float y, float r, int segments) { float f_segments = (float)segments; @@ -1322,373 +654,6 @@ void draw_circle(float x, float y, float r, int segments) } } -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); -} - - -static void render_player( - const obj_player_character *prev_char, - const obj_player_character *player_char, - const obj_player_info *prev_info, - const obj_player_info *player_info - ) -{ - obj_player_character prev; - obj_player_character player; - prev = *prev_char; - player = *player_char; - - obj_player_info info = *player_info; - - float intratick = client_intratick(); - float ticktime = client_ticktime(); - - if(player.health < 0) // dont render dead players - return; - - if(info.local && config.cl_predict) - { - if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over)) - { - } - else - { - // apply predicted results - predicted_player.write(&player); - predicted_prev_player.write(&prev); - intratick = client_intrapredtick(); - } - } - - vec2 direction = get_direction(player.angle); - float angle = player.angle/256.0f; - vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); - - 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; - - // evaluate animation - float walk_time = fmod(position.x, 100.0f)/100.0f; - animstate state; - anim_eval(&data->animations[ANIM_BASE], 0, &state); - - if(inair) - anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here - else if(stationary) - anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here - else - anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f); - - if (player.weapon == WEAPON_HAMMER) - { - float a = clamp((client_tick()-player.attacktick+ticktime)/10.0f, 0.0f, 1.0f); - anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f); - } - if (player.weapon == WEAPON_NINJA) - { - float a = clamp((client_tick()-player.attacktick+ticktime)/40.0f, 0.0f, 1.0f); - anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f); - } - - // 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(local_info && player_char->hooked_player == local_info->clientid) - { - hook_pos = mix(vec2(predicted_prev_player.pos.x, predicted_prev_player.pos.y), - vec2(predicted_player.pos.x, predicted_player.pos.y), client_intrapredtick()); - } - 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); - for(float f = 24; f < d; f += 24) - { - vec2 p = hook_pos + dir*f; - gfx_quads_draw(p.x, p.y,24,16); - } - - gfx_quads_setrotation(0); - gfx_quads_end(); - - render_hand(&client_datas[info.clientid].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[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; - p.y += data->weapons[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[iw].offsetx; - } - else - { - gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); - } - draw_sprite(p.x, p.y, data->weapons[iw].visual_size); - } - else if (player.weapon == WEAPON_NINJA) - { - p = position; - p.y += data->weapons[iw].offsety; - - if(direction.x < 0) - { - gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); - p.x -= data->weapons[iw].offsetx; - } - else - { - gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); - } - draw_sprite(p.x, p.y, data->weapons[iw].visual_size); - - // HADOKEN - if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites) - { - int itex = rand() % data->weapons[iw].nummuzzlesprites; - float alpha = 1.0f; - if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) - { - 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[iw].sprite_muzzle[itex].psprite, 0); - vec2 diry(-dir.y,dir.x); - p = position; - float offsetx = data->weapons[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[iw].offsetx - dir*recoil*10.0f; - p.y += data->weapons[iw].offsety; - draw_sprite(p.x, p.y, data->weapons[iw].visual_size); - } - - if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) - { - // check if we're firing stuff - if (true)//prev.attackticks) - { - float alpha = 0.0f; - int phase1tick = (client_tick() - player.attacktick); - if (phase1tick < (data->weapons[iw].muzzleduration + 3)) - { - float t = ((((float)phase1tick) + intratick)/(float)data->weapons[iw].muzzleduration); - alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); - } - - int itex = rand() % data->weapons[iw].nummuzzlesprites; - if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) - { - float offsety = -data->weapons[iw].muzzleoffsety; - select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, 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[iw].muzzleoffsetx + diry * offsety; - - draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons[iw].visual_size); - /*gfx_setcolor(1.0f,1.0f,1.0f,alpha); - vec2 diry(-dir.y,dir.x); - p += dir * muzzleparams[player.weapon].offsetx + diry * offsety; - gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/ - } - } - } - gfx_quads_end(); - - switch (player.weapon) - { - case WEAPON_GUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break; - case WEAPON_SHOTGUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-5, 4)); break; - case WEAPON_ROCKET: render_hand(&client_datas[info.clientid].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 = client_datas[info.clientid].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 the tee - render_tee(&state, &client_datas[info.clientid].render_info, player.emote, direction, position); - - if(player.state == STATE_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 (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick()) - { - gfx_texture_set(data->images[IMAGE_EMOTICONS].id); - gfx_quads_begin(); - - int since_start = client_tick() - client_datas[info.clientid].emoticon_start; - int from_end = client_datas[info.clientid].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 + client_datas[info.clientid].emoticon); - gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); - gfx_quads_end(); - } - - // render name plate - if(!info.local && config.cl_nameplates) - { - //gfx_text_color - float a = 1; - if(config.cl_nameplates_always == 0) - a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); - - const char *name = client_datas[info.clientid].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); - gfx_text_color(1,1,1,1); - } -} - void render_moon(float x, float y) { gfx_texture_set(data->images[IMAGE_MOON].id); @@ -2570,8 +1535,6 @@ void render_game() // pseudo format // ZOOM ZOOM float zoom = 3.0; - if(local_character) - cl_effects.getzoom(client_tick(), client_intratick(), local_character); // DEBUG TESTING if(zoom > 3.01f) @@ -2989,7 +1952,7 @@ void render_game() if (menu_active) { - modmenu_render(); + menu_render(); return; } @@ -3073,211 +2036,3 @@ void render_game() } } -extern "C" void modc_render() -{ - // this should be moved around abit - if(client_state() == CLIENTSTATE_ONLINE) - { - render_game(); - - // handle team switching - // TODO: FUGLY!!! - if(config.cl_team != -10) - { - msg_pack_start(MSG_SETTEAM, MSGFLAG_VITAL); - msg_pack_int(config.cl_team); - msg_pack_end(); - client_send_msg(); - } - } - else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING) - { - modmenu_render(); - } - - // - config.cl_team = -10; -} - - -void menu_do_disconnected(); -void menu_do_connecting(); -void menu_do_connected(); - -extern "C" int modc_snap_input(int *data) -{ - picked_up_weapon = -1; - - if(!input_target_lock) - { - 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; - } - input_target_lock = 0; - - if(chat_mode != CHATMODE_NONE) - input_data.state = STATE_CHATTING; - else if(menu_active) - input_data.state = STATE_IN_MENU; - else - { - input_data.state = STATE_PLAYING; - input_data.left = inp_key_state(config.key_move_left); - input_data.right = inp_key_state(config.key_move_right); - input_data.hook = inp_key_state(config.key_hook); - input_data.jump = inp_key_state(config.key_jump); - } - - // stress testing - if(config.dbg_stress) - { - float t = client_localtime(); - mem_zero(&input_data, sizeof(input_data)); - - input_data.left = ((int)t/2)&1; - input_data.right = ((int)t/2+1)&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); - } - - // copy and return size - mem_copy(data, &input_data, sizeof(input_data)); - return sizeof(input_data); -} - -extern "C" void modc_statechange(int state, int old) -{ - clear_object_pointers(); - - if(state == CLIENTSTATE_OFFLINE) - { - menu_do_disconnected(); - menu_game_active = false; - } - else if(state == CLIENTSTATE_LOADING) - menu_do_connecting(); - else if(state == CLIENTSTATE_CONNECTING) - menu_do_connecting(); - else if (state == CLIENTSTATE_ONLINE) - { - menu_active = false; - menu_game_active = true; - snapshot_count = 0; - - menu_do_connected(); - } -} - -extern "C" void modc_message(int msg) -{ - if(msg == MSG_CHAT) - { - int cid = msg_unpack_int(); - int team = msg_unpack_int(); - const char *message = msg_unpack_string(); - dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message); - chat_add_line(cid, team, message); - - if(cid >= 0) - snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0); - else - snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0); - } - else if(msg == MSG_SETINFO) - { - int cid = msg_unpack_int(); - const char *name = msg_unpack_string(); - const char *skinname = msg_unpack_string(); - - strncpy(client_datas[cid].name, name, 64); - strncpy(client_datas[cid].skin_name, skinname, 64); - - int use_custom_color = msg_unpack_int(); - client_datas[cid].skin_info.color_body = skin_get_color(msg_unpack_int()); - client_datas[cid].skin_info.color_feet = skin_get_color(msg_unpack_int()); - client_datas[cid].skin_info.size = 64; - - // find new skin - client_datas[cid].skin_id = skin_find(client_datas[cid].skin_name); - if(client_datas[cid].skin_id < 0) - client_datas[cid].skin_id = 0; - - if(use_custom_color) - client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->color_texture; - else - { - client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->org_texture; - client_datas[cid].skin_info.color_body = vec4(1,1,1,1); - client_datas[cid].skin_info.color_feet = vec4(1,1,1,1); - } - - client_datas[cid].update_render_info(); - } - else if(msg == MSG_WEAPON_PICKUP) - { - int weapon = msg_unpack_int(); - - picked_up_weapon = weapon+1; - } - else if(msg == MSG_READY_TO_ENTER) - { - client_entergame(); - } - else if(msg == MSG_KILLMSG) - { - killmsg_current = (killmsg_current+1)%killmsg_max; - killmsgs[killmsg_current].killer = msg_unpack_int(); - killmsgs[killmsg_current].victim = msg_unpack_int(); - killmsgs[killmsg_current].weapon = msg_unpack_int(); - killmsgs[killmsg_current].mode_special = msg_unpack_int(); - killmsgs[killmsg_current].tick = client_tick(); - } - else if (msg == MSG_EMOTICON) - { - int cid = msg_unpack_int(); - int emoticon = msg_unpack_int(); - client_datas[cid].emoticon = emoticon; - client_datas[cid].emoticon_start = client_tick(); - } - else if(msg == MSG_SOUND_GLOBAL) - { - int soundid = msg_unpack_int(); - snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0)); - } -} - -extern "C" void modc_connected() -{ - // init some stuff - col_init(32); - img_init(); - tilemap_init(); - chat_reset(); - - proj_particles.reset(); - - clear_object_pointers(); - last_new_predicted_tick = -1; - - for(int i = 0; i < MAX_CLIENTS; i++) - { - client_datas[i].name[0] = 0; - client_datas[i].team = 0; - client_datas[i].emoticon = 0; - client_datas[i].emoticon_start = -1; - } - - for(int i = 0; i < killmsg_max; i++) - killmsgs[i].tick = -100000; - - send_info(true); -} - -extern "C" const char *modc_net_version() { return TEEWARS_NETVERSION; } diff --git a/src/game/client/gc_client.h b/src/game/client/gc_client.h new file mode 100644 index 00000000..d34ccc78 --- /dev/null +++ b/src/game/client/gc_client.h @@ -0,0 +1,93 @@ +#include +#include +#include + +#include + +// sound channels +enum +{ + CHN_GUI=0, + CHN_MUSIC, + CHN_WORLD, + CHN_GLOBAL, +}; + +extern struct data_container *data; + +extern vec2 mouse_pos; +extern vec2 local_character_pos; +extern vec2 local_target_pos; + +// snap pointers +extern const obj_player_character *local_character; +extern const obj_player_character *local_prev_character; +extern const obj_player_info *local_info; +extern const obj_flag *flags[2]; +extern const obj_game *gameobj; + +// predicted players +extern player_core predicted_prev_player; +extern player_core predicted_player; + +// input +extern int picked_up_weapon; +extern player_input input_data; +extern int input_target_lock; + +// chat +enum +{ + CHATMODE_NONE=0, + CHATMODE_ALL, + CHATMODE_TEAM, + CHATMODE_CONSOLE, + CHATMODE_REMOTECONSOLE, +}; + +extern int chat_mode; +void chat_add_line(int client_id, int team, const char *line); +void chat_reset(); + +// client data +struct client_data +{ + char name[64]; + char skin_name[64]; + int skin_id; + int skin_color; + int team; + int emoticon; + int emoticon_start; + player_core predicted; + + tee_render_info skin_info; // this is what the server reports + tee_render_info render_info; // this is what we use + + void update_render_info(); +}; + +extern client_data client_datas[MAX_CLIENTS]; + +// 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; +}; + +const int killmsg_max = 5; +extern killmsg killmsgs[killmsg_max]; +extern int killmsg_current; + +// various helpers +void snd_play_random(int chn, int setid, float vol, vec2 pos); +void process_events(int snaptype); +void clear_object_pointers(); +void reset_projectile_particles(); +void send_info(bool start); + +void effect_air_jump(vec2 pos); diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp new file mode 100644 index 00000000..febb6478 --- /dev/null +++ b/src/game/client/gc_hooks.cpp @@ -0,0 +1,500 @@ +#include + +#include + +extern "C" { + #include + #include +}; + +#include +#include +#include + +#include "gc_client.h" +#include "gc_skin.h" +#include "gc_render.h" +#include "gc_mapres_image.h" +#include "gc_mapres_tilemap.h" + +extern unsigned char internal_data[]; + +extern void menu_init(); +extern bool menu_active; +extern bool menu_game_active; + + +extern "C" void modc_init() +{ + static FONT_SET default_font; + + 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 usage: %d", gfx_memory_usage()-before); + + gfx_text_set_default_font(&default_font); + + menu_init(); + + // setup sound channels + snd_set_channel(CHN_GUI, 1.0f, 0.0f); + snd_set_channel(CHN_MUSIC, 1.0f, 0.0f); + snd_set_channel(CHN_WORLD, 0.9f, 1.0f); + snd_set_channel(CHN_GLOBAL, 1.0f, 0.0f); + + // load the data container + data = load_data_from_memory(internal_data); + + // TODO: should be removed + snd_set_listener_pos(0.0f, 0.0f); + + float total = data->num_sounds+data->num_images; + float current = 0; + + // load textures + for(int i = 0; i < data->num_images; i++) + { + render_loading(current/total); + data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO); + current++; + } + + // load sounds + for(int s = 0; s < data->num_sounds; s++) + { + render_loading(current/total); + for(int i = 0; i < data->sounds[s].num_sounds; i++) + { + int id; + //if (strcmp(data->sounds[s].sounds[i].filename + strlen(data->sounds[s].sounds[i].filename) - 3, ".wv") == 0) + id = snd_load_wv(data->sounds[s].sounds[i].filename); + //else + // id = snd_load_wav(data->sounds[s].sounds[i].filename); + + data->sounds[s].sounds[i].id = id; + } + + current++; + } + + skin_init(); +} + +extern "C" void modc_entergame() +{ +} + +extern "C" void modc_shutdown() +{ + // shutdown the menu +} + + +player_core predicted_prev_player; +player_core predicted_player; +static int predicted_tick = 0; +static int last_new_predicted_tick = -1; + +extern "C" void modc_predict() +{ + player_core before_prev_player = predicted_prev_player; + player_core before_player = predicted_player; + + // repredict player + world_core world; + int local_cid = -1; + + // search for players + for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++) + { + SNAP_ITEM item; + const void *data = snap_get_item(SNAP_CURRENT, i, &item); + int client_id = item.id; + + if(item.type == OBJTYPE_PLAYER_CHARACTER) + { + const obj_player_character *character = (const obj_player_character *)data; + client_datas[client_id].predicted.world = &world; + world.players[client_id] = &client_datas[client_id].predicted; + + client_datas[client_id].predicted.read(character); + } + else if(item.type == OBJTYPE_PLAYER_INFO) + { + const obj_player_info *info = (const obj_player_info *)data; + if(info->local) + local_cid = client_id; + } + } + + // predict + for(int tick = client_tick()+1; tick <= client_predtick(); tick++) + { + // fetch the local + if(tick == client_predtick() && world.players[local_cid]) + predicted_prev_player = *world.players[local_cid]; + + // first calculate where everyone should move + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!world.players[c]) + continue; + + mem_zero(&world.players[c]->input, sizeof(world.players[c]->input)); + if(local_cid == c) + { + // apply player input + int *input = client_get_input(tick); + if(input) + world.players[c]->input = *((player_input*)input); + } + + world.players[c]->tick(); + } + + // move all players and quantize their data + for(int c = 0; c < MAX_CLIENTS; c++) + { + if(!world.players[c]) + continue; + + + // TODO: this should be moved into the g_game + // but not done to preserve the nethash + if(length(world.players[c]->vel) > 150.0f) + world.players[c]->vel = normalize(world.players[c]->vel) * 150.0f; + + world.players[c]->move(); + world.players[c]->quantize(); + } + + if(tick > last_new_predicted_tick) + { + last_new_predicted_tick = tick; + + if(local_cid != -1 && world.players[local_cid]) + { + vec2 pos = world.players[local_cid]->pos; + int events = world.players[local_cid]->triggered_events; + if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + if(events&COREEVENT_AIR_JUMP) + { + effect_air_jump(pos); + snd_play_random(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) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos); + //if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos); + } + + + /* + dbg_msg("predict", "%d %d %d", tick, + (int)world.players[c]->pos.x, (int)world.players[c]->pos.y, + (int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/ + } + + if(tick == client_predtick() && world.players[local_cid]) + predicted_player = *world.players[local_cid]; + } + + if(config.debug && predicted_tick == client_predtick()) + { + if(predicted_player.pos.x != before_player.pos.x || + predicted_player.pos.y != before_player.pos.y) + { + dbg_msg("client", "prediction error, (%d %d) (%d %d)", + (int)before_player.pos.x, (int)before_player.pos.y, + (int)predicted_player.pos.x, (int)predicted_player.pos.y); + } + + if(predicted_prev_player.pos.x != before_prev_player.pos.x || + predicted_prev_player.pos.y != before_prev_player.pos.y) + { + dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", + (int)before_prev_player.pos.x, (int)before_prev_player.pos.y, + (int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y); + } + } + + predicted_tick = client_predtick(); +} + + +extern "C" void modc_newsnapshot() +{ + static int snapshot_count = 0; + snapshot_count++; + + process_events(SNAP_CURRENT); + + if(config.dbg_stress) + { + if((client_tick()%250) == 0) + { + msg_pack_start(MSG_SAY, MSGFLAG_VITAL); + msg_pack_int(-1); + msg_pack_string("galenskap!!!!", 512); + msg_pack_end(); + client_send_msg(); + } + } + + clear_object_pointers(); + + // setup world view + { + // 1. fetch local player + // 2. set him to the center + 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 == OBJTYPE_PLAYER_INFO) + { + const obj_player_info *info = (const obj_player_info *)data; + + client_datas[info->clientid].team = info->team; + + if(info->local) + { + local_info = info; + const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id); + if(data) + { + local_character = (const obj_player_character *)data; + local_character_pos = vec2(local_character->x, local_character->y); + + const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id); + if(p) + local_prev_character = (obj_player_character *)p; + } + } + } + else if(item.type == OBJTYPE_GAME) + gameobj = (obj_game *)data; + else if(item.type == OBJTYPE_FLAG) + { + flags[item.id%2] = (const obj_flag *)data; + } + } + } + + for(int i = 0; i < MAX_CLIENTS; i++) + client_datas[i].update_render_info(); +} + + + +extern "C" void modc_render() +{ + // this should be moved around abit + if(client_state() == CLIENTSTATE_ONLINE) + { + render_game(); + + // handle team switching + // TODO: FUGLY!!! + if(config.cl_team != -10) + { + msg_pack_start(MSG_SETTEAM, MSGFLAG_VITAL); + msg_pack_int(config.cl_team); + msg_pack_end(); + client_send_msg(); + } + } + else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING) + { + menu_render(); + } + + // + config.cl_team = -10; +} + + +extern "C" int modc_snap_input(int *data) +{ + picked_up_weapon = -1; + + if(!input_target_lock) + { + 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; + } + input_target_lock = 0; + + if(chat_mode != CHATMODE_NONE) + input_data.state = STATE_CHATTING; + else if(menu_active) + input_data.state = STATE_IN_MENU; + else + { + input_data.state = STATE_PLAYING; + input_data.left = inp_key_state(config.key_move_left); + input_data.right = inp_key_state(config.key_move_right); + input_data.hook = inp_key_state(config.key_hook); + input_data.jump = inp_key_state(config.key_jump); + } + + // stress testing + if(config.dbg_stress) + { + float t = client_localtime(); + mem_zero(&input_data, sizeof(input_data)); + + input_data.left = ((int)t/2)&1; + input_data.right = ((int)t/2+1)&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); + } + + // copy and return size + mem_copy(data, &input_data, sizeof(input_data)); + return sizeof(input_data); +} + +void menu_do_disconnected(); +void menu_do_connecting(); +void menu_do_connected(); + +extern "C" void modc_statechange(int state, int old) +{ + clear_object_pointers(); + + if(state == CLIENTSTATE_OFFLINE) + { + menu_do_disconnected(); + menu_game_active = false; + } + else if(state == CLIENTSTATE_LOADING) + menu_do_connecting(); + else if(state == CLIENTSTATE_CONNECTING) + menu_do_connecting(); + else if (state == CLIENTSTATE_ONLINE) + { + menu_active = false; + menu_game_active = true; + //snapshot_count = 0; + + menu_do_connected(); + } +} + +extern "C" void modc_message(int msg) +{ + if(msg == MSG_CHAT) + { + int cid = msg_unpack_int(); + int team = msg_unpack_int(); + const char *message = msg_unpack_string(); + dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message); + chat_add_line(cid, team, message); + + if(cid >= 0) + snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0); + else + snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0); + } + else if(msg == MSG_SETINFO) + { + int cid = msg_unpack_int(); + const char *name = msg_unpack_string(); + const char *skinname = msg_unpack_string(); + + strncpy(client_datas[cid].name, name, 64); + strncpy(client_datas[cid].skin_name, skinname, 64); + + int use_custom_color = msg_unpack_int(); + client_datas[cid].skin_info.color_body = skin_get_color(msg_unpack_int()); + client_datas[cid].skin_info.color_feet = skin_get_color(msg_unpack_int()); + client_datas[cid].skin_info.size = 64; + + // find new skin + client_datas[cid].skin_id = skin_find(client_datas[cid].skin_name); + if(client_datas[cid].skin_id < 0) + client_datas[cid].skin_id = 0; + + if(use_custom_color) + client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->color_texture; + else + { + client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->org_texture; + client_datas[cid].skin_info.color_body = vec4(1,1,1,1); + client_datas[cid].skin_info.color_feet = vec4(1,1,1,1); + } + + client_datas[cid].update_render_info(); + } + else if(msg == MSG_WEAPON_PICKUP) + { + int weapon = msg_unpack_int(); + + picked_up_weapon = weapon+1; + } + else if(msg == MSG_READY_TO_ENTER) + { + client_entergame(); + } + else if(msg == MSG_KILLMSG) + { + killmsg_current = (killmsg_current+1)%killmsg_max; + killmsgs[killmsg_current].killer = msg_unpack_int(); + killmsgs[killmsg_current].victim = msg_unpack_int(); + killmsgs[killmsg_current].weapon = msg_unpack_int(); + killmsgs[killmsg_current].mode_special = msg_unpack_int(); + killmsgs[killmsg_current].tick = client_tick(); + } + else if (msg == MSG_EMOTICON) + { + int cid = msg_unpack_int(); + int emoticon = msg_unpack_int(); + client_datas[cid].emoticon = emoticon; + client_datas[cid].emoticon_start = client_tick(); + } + else if(msg == MSG_SOUND_GLOBAL) + { + int soundid = msg_unpack_int(); + snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0)); + } +} + +extern "C" void modc_connected() +{ + // init some stuff + col_init(32); + + img_init(); + tilemap_init(); + chat_reset(); + + reset_projectile_particles(); + + clear_object_pointers(); + last_new_predicted_tick = -1; + + for(int i = 0; i < MAX_CLIENTS; i++) + { + client_datas[i].name[0] = 0; + client_datas[i].team = 0; + client_datas[i].emoticon = 0; + client_datas[i].emoticon_start = -1; + } + + for(int i = 0; i < killmsg_max; i++) + killmsgs[i].tick = -100000; + + send_info(true); +} + +extern "C" const char *modc_net_version() { return TEEWARS_NETVERSION; } diff --git a/src/game/client/gc_mapres_tilemap.cpp b/src/game/client/gc_mapres_tilemap.cpp index d94add17..99976b64 100644 --- a/src/game/client/gc_mapres_tilemap.cpp +++ b/src/game/client/gc_mapres_tilemap.cpp @@ -5,6 +5,12 @@ #include "gc_mapres_image.h" #include "../g_mapres.h" +enum +{ + TILEFLAG_VFLIP=1, + TILEFLAG_HFLIP=2, +}; + int tilemap_init() { return 0; diff --git a/src/game/client/gc_mapres_tilemap.h b/src/game/client/gc_mapres_tilemap.h index a13495ed..5dfbd6b5 100644 --- a/src/game/client/gc_mapres_tilemap.h +++ b/src/game/client/gc_mapres_tilemap.h @@ -8,12 +8,6 @@ int tilemap_init(); // renders the tilemaps void tilemap_render(float scale, int fg); -enum -{ - TILEFLAG_VFLIP=1, - TILEFLAG_HFLIP=2, -}; - struct mapres_tilemap { int image; diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp index 34558402..e0c2a19f 100644 --- a/src/game/client/gc_menu.cpp +++ b/src/game/client/gc_menu.cpp @@ -23,6 +23,7 @@ extern "C" { #include "../generated/gc_data.h" #include "gc_render.h" +#include "gc_anim.h" #include "gc_skin.h" #include "gc_ui.h" #include @@ -73,11 +74,6 @@ enum PAGE_SYSTEM, }; -extern void select_sprite(int id, int flags=0, int sx=0, int sy=0); - -extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); -extern void draw_round_rect(float x, float y, float w, float h, float r); - static void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding) { gfx_texture_set(-1); @@ -259,12 +255,6 @@ int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hid r = 1; } - int box_type; - if (ui_active_item() == id || ui_hot_item() == id || ui_last_active_item() == id) - box_type = GUI_BOX_SCREEN_INFO; - else - box_type = GUI_BOX_SCREEN_TEXTBOX; - bool just_got_active = false; if(ui_active_item() == id) @@ -1972,7 +1962,7 @@ int menu2_render() return 0; } -void modmenu_render() +void menu_render() { static int mouse_x = 0; static int mouse_y = 0; diff --git a/src/game/client/gc_menu.h b/src/game/client/gc_menu.h index 5c68c53d..bd6352de 100644 --- a/src/game/client/gc_menu.h +++ b/src/game/client/gc_menu.h @@ -1,7 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #ifndef __MENU_H #define __MENU_H - +/* void draw_image_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); void draw_single_part_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); @@ -12,5 +12,5 @@ int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int int ui_do_check_box(void *id, float x, float y, float w, float h, int value); int do_scroll_bar_horiz(void *id, float x, float y, float width, int steps, int last_index); int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int last_index); - +*/ #endif diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp new file mode 100644 index 00000000..9a4c35d5 --- /dev/null +++ b/src/game/client/gc_render.cpp @@ -0,0 +1,237 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include "../generated/gc_data.h" +#include "../g_protocol.h" +#include "../g_math.h" +#include "gc_render.h" +#include "gc_anim.h" +#include "gc_client.h" + +static float sprite_w_scale; +static float sprite_h_scale; + +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 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, info->color_body.a); + 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 + gfx_setcolor(info->color_feet.r, info->color_feet.g, info->color_feet.b, info->color_feet.a); + select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, 0); + + keyframe *foot = f ? &anim->front_foot : &anim->back_foot; + + float w = basesize; + float h = basesize/2; + + gfx_quads_setrotation(foot->angle*pi*2); + 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(1300*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; +} diff --git a/src/game/client/gc_render.h b/src/game/client/gc_render.h index fc85d49a..2f17f6b5 100644 --- a/src/game/client/gc_render.h +++ b/src/game/client/gc_render.h @@ -1,17 +1,9 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -struct animstate -{ - keyframe body; - keyframe back_foot; - keyframe front_foot; - keyframe attach; -}; +#ifndef GAME_CLIENT_RENDER_H +#define GAME_CLIENT_RENDER_H -void anim_seq_eval(sequence *seq, float time, keyframe *frame); -void anim_eval(animation *anim, float time, animstate *state); -void anim_add_keyframe(keyframe *seq, keyframe *added, float amount); -void anim_add(animstate *state, animstate *added, float amount); -void anim_eval_add(animstate *state, animation *anim, float time, float amount); +#include "../g_vmath.h" +#include "../g_mapitems.h" struct tee_render_info { @@ -21,4 +13,46 @@ struct tee_render_info float size; }; -void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos); +// sprite renderings +enum +{ + SPRITE_FLAG_FLIP_Y=1, + SPRITE_FLAG_FLIP_X=2, +}; + +typedef 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); + +// larger rendering methods +void menu_render(); +void render_game(); +void render_world(float center_x, float center_y, float zoom); +void render_loading(float percent); + +// object render methods (gc_render_obj.cpp) +void render_tee(class animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos); +void render_flag(const struct obj_flag *prev, const struct obj_flag *current); +void render_powerup(const struct obj_powerup *prev, const struct obj_powerup *current); +void render_projectile(const struct obj_projectile *current, int itemid); +void render_player( + const struct obj_player_character *prev_char, const struct obj_player_character *player_char, + const struct obj_player_info *prev_info, const struct obj_player_info *player_info); + +// map render methods (gc_render_map.cpp) +void render_quads(QUAD *quads, int num_quads); +void render_tilemap(TILE *tiles, int w, int h, float scale); + +// 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 diff --git a/src/game/client/gc_render_map.cpp b/src/game/client/gc_render_map.cpp new file mode 100644 index 00000000..fcb8c272 --- /dev/null +++ b/src/game/client/gc_render_map.cpp @@ -0,0 +1,167 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "../g_math.h" +#include "gc_client.h" + +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) +{ + gfx_quads_begin(); + float conv = 1/255.0f; + for(int i = 0; i < num_quads; i++) + { + QUAD *q = &quads[i]; + + 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 r=1, g=1, b=1, a=1; + float offset_x = 0; + float offset_y = 0; + float rot = 0; + + /* + // TODO: fix this + if(editor.animate) + { + if(q->pos_env >= 0 && q->pos_env < editor.map.envelopes.len()) + { + ENVELOPE *e = editor.map.envelopes[q->pos_env]; + float t = editor.animate_time+q->pos_env_offset/1000.0f; + offset_x = e->eval(t, 0); + offset_y = e->eval(t, 1); + rot = e->eval(t, 2); + } + + if(q->color_env >= 0 && q->color_env < editor.map.envelopes.len()) + { + ENVELOPE *e = editor.map.envelopes[q->color_env]; + float t = editor.animate_time+q->color_env_offset/1000.0f; + r = e->eval(t, 0); + g = e->eval(t, 1); + b = e->eval(t, 2); + a = e->eval(t, 3); + } + }*/ + + 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) +{ + //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); + + // 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(); + + 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(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; + 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); + } + } + + gfx_quads_end(); +} diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/gc_render_obj.cpp new file mode 100644 index 00000000..ba248907 --- /dev/null +++ b/src/game/client/gc_render_obj.cpp @@ -0,0 +1,478 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include "../generated/gc_data.h" +#include "../g_protocol.h" +#include "../g_math.h" +#include "gc_render.h" +#include "gc_anim.h" +#include "gc_client.h" + + +void render_projectile(const obj_projectile *current, int itemid) +{ + /* + if(debug_firedelay) + { + debug_firedelay = time_get()-debug_firedelay; + dbg_msg("game", "firedelay=%.2f ms", debug_firedelay/(float)time_freq()*1000.0f); + debug_firedelay = 0; + }*/ + + gfx_texture_set(data->images[IMAGE_GAME].id); + gfx_quads_begin(); + + // get positions + float gravity = -400; + if(current->type != WEAPON_ROCKET) + gravity = -100; + if(current->type == WEAPON_BOMB) + gravity = 0; + + float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED; + vec2 startpos(current->x, current->y); + vec2 startvel(current->vx, current->vy); + vec2 pos = calc_pos(startpos, startvel, gravity, ct); + vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f); + + select_sprite(data->weapons[current->type%data->num_weapons].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 + //proj_particles.addparticle(current->type, itemid, pos, vel); + + if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0); + + // TODO: do this, but nice + //temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f); + + gfx_quads_draw(pos.x, pos.y, 32, 32); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +void render_powerup(const obj_powerup *prev, const obj_powerup *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[current->subtype%data->num_weapons].sprite_body); + size = data->weapons[current->subtype%data->num_weapons].visual_size; + } + else + { + const int c[] = { + SPRITE_POWERUP_HEALTH, + SPRITE_POWERUP_ARMOR, + SPRITE_POWERUP_WEAPON, + SPRITE_POWERUP_NINJA, + SPRITE_POWERUP_TIMEFIELD + }; + select_sprite(c[current->type]); + + if(c[current->type] == SPRITE_POWERUP_NINJA) + { + /* + proj_particles.addparticle(0, 0, + pos+vec2((frandom()-0.5f)*80.0f, (frandom()-0.5f)*20.0f), + vec2((frandom()-0.5f)*10.0f, (frandom()-0.5f)*10.0f));*/ + 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 render_flag(const obj_flag *prev, const obj_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()); + + if(local_info && current->carried_by == local_info->clientid) + pos = local_character_pos; + + //gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1); + //draw_sprite(pos.x, pos.y, size); + gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2); + gfx_quads_end(); +} + + + +static void 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(); +} + +void render_player( + const obj_player_character *prev_char, + const obj_player_character *player_char, + const obj_player_info *prev_info, + const obj_player_info *player_info + ) +{ + obj_player_character prev; + obj_player_character player; + prev = *prev_char; + player = *player_char; + + obj_player_info info = *player_info; + + float intratick = client_intratick(); + float ticktime = client_ticktime(); + + if(player.health < 0) // dont render dead players + return; + + if(info.local && config.cl_predict) + { + if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over)) + { + } + else + { + // apply predicted results + predicted_player.write(&player); + predicted_prev_player.write(&prev); + intratick = client_intrapredtick(); + } + } + + vec2 direction = get_direction(player.angle); + float angle = player.angle/256.0f; + vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick); + + 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; + + // evaluate animation + float walk_time = fmod(position.x, 100.0f)/100.0f; + animstate state; + anim_eval(&data->animations[ANIM_BASE], 0, &state); + + if(inair) + anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here + else if(stationary) + anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here + else + anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f); + + if (player.weapon == WEAPON_HAMMER) + { + float a = clamp((client_tick()-player.attacktick+ticktime)/10.0f, 0.0f, 1.0f); + anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f); + } + if (player.weapon == WEAPON_NINJA) + { + float a = clamp((client_tick()-player.attacktick+ticktime)/40.0f, 0.0f, 1.0f); + anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f); + } + + // 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(local_info && player_char->hooked_player == local_info->clientid) + { + hook_pos = mix(vec2(predicted_prev_player.pos.x, predicted_prev_player.pos.y), + vec2(predicted_player.pos.x, predicted_player.pos.y), client_intrapredtick()); + } + 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); + for(float f = 24; f < d; f += 24) + { + vec2 p = hook_pos + dir*f; + gfx_quads_draw(p.x, p.y,24,16); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); + + render_hand(&client_datas[info.clientid].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[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; + p.y += data->weapons[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[iw].offsetx; + } + else + { + gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + } + draw_sprite(p.x, p.y, data->weapons[iw].visual_size); + } + else if (player.weapon == WEAPON_NINJA) + { + p = position; + p.y += data->weapons[iw].offsety; + + if(direction.x < 0) + { + gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2); + p.x -= data->weapons[iw].offsetx; + } + else + { + gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2); + } + draw_sprite(p.x, p.y, data->weapons[iw].visual_size); + + // HADOKEN + if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites) + { + int itex = rand() % data->weapons[iw].nummuzzlesprites; + float alpha = 1.0f; + if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) + { + 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[iw].sprite_muzzle[itex].psprite, 0); + vec2 diry(-dir.y,dir.x); + p = position; + float offsetx = data->weapons[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[iw].offsetx - dir*recoil*10.0f; + p.y += data->weapons[iw].offsety; + draw_sprite(p.x, p.y, data->weapons[iw].visual_size); + } + + if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN) + { + // check if we're firing stuff + if (true)//prev.attackticks) + { + float alpha = 0.0f; + int phase1tick = (client_tick() - player.attacktick); + if (phase1tick < (data->weapons[iw].muzzleduration + 3)) + { + float t = ((((float)phase1tick) + intratick)/(float)data->weapons[iw].muzzleduration); + alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); + } + + int itex = rand() % data->weapons[iw].nummuzzlesprites; + if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite) + { + float offsety = -data->weapons[iw].muzzleoffsety; + select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, 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[iw].muzzleoffsetx + diry * offsety; + + draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons[iw].visual_size); + /*gfx_setcolor(1.0f,1.0f,1.0f,alpha); + vec2 diry(-dir.y,dir.x); + p += dir * muzzleparams[player.weapon].offsetx + diry * offsety; + gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/ + } + } + } + gfx_quads_end(); + + switch (player.weapon) + { + case WEAPON_GUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break; + case WEAPON_SHOTGUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-5, 4)); break; + case WEAPON_ROCKET: render_hand(&client_datas[info.clientid].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 = client_datas[info.clientid].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 the tee + render_tee(&state, &client_datas[info.clientid].render_info, player.emote, direction, position); + + if(player.state == STATE_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 (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick()) + { + gfx_texture_set(data->images[IMAGE_EMOTICONS].id); + gfx_quads_begin(); + + int since_start = client_tick() - client_datas[info.clientid].emoticon_start; + int from_end = client_datas[info.clientid].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 + client_datas[info.clientid].emoticon); + gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h); + gfx_quads_end(); + } + + // render name plate + if(!info.local && config.cl_nameplates) + { + //gfx_text_color + float a = 1; + if(config.cl_nameplates_always == 0) + a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f); + + const char *name = client_datas[info.clientid].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); + gfx_text_color(1,1,1,1); + } +} diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp new file mode 100644 index 00000000..8c6c0c73 --- /dev/null +++ b/src/game/editor/ed_editor.cpp @@ -0,0 +1,2441 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include + +extern "C" { + #include + #include + #include + #include +} + +#include +#include +#include +//#include "game/mapres_col.h" +#include +#include +#include + +#include "ed_editor.hpp" + +static int checker_texture = 0; +static int background_texture = 0; +static int cursor_texture = 0; +static int entities_texture = 0; + + +EDITOR editor; + +LAYERGROUP::LAYERGROUP() +{ + name = ""; + visible = true; + game_group = false; + offset_x = 0; + offset_y = 0; + parallax_x = 100; + parallax_y = 100; +} + +LAYERGROUP::~LAYERGROUP() +{ + clear(); +} + +void LAYERGROUP::convert(RECT *rect) +{ + rect->x += offset_x; + rect->y += offset_y; +} + +void LAYERGROUP::mapping(float *points) +{ + mapscreen_to_world( + editor.world_offset_x, editor.world_offset_y, + parallax_x/100.0f, parallax_y/100.0f, + offset_x, offset_y, + gfx_screenaspect(), editor.world_zoom, points); + + points[0] += editor.editor_offset_x; + points[1] += editor.editor_offset_y; + points[2] += editor.editor_offset_x; + points[3] += editor.editor_offset_y; +} + +void LAYERGROUP::mapscreen() +{ + float points[4]; + mapping(points); + gfx_mapscreen(points[0], points[1], points[2], points[3]); +} + +void LAYERGROUP::render() +{ + mapscreen(); + + for(int i = 0; i < layers.len(); i++) + { + if(layers[i]->visible && layers[i] != editor.game_layer) + layers[i]->render(); + } +} + +bool LAYERGROUP::is_empty() const { return layers.len() == 0; } +void LAYERGROUP::clear() { layers.deleteall(); } +void LAYERGROUP::add_layer(LAYER *l) { layers.add(l); } + +void LAYERGROUP::delete_layer(int index) +{ + if(index < 0 || index >= layers.len()) return; + delete layers[index]; + layers.removebyindex(index); +} + +void LAYERGROUP::get_size(float *w, float *h) +{ + *w = 0; *h = 0; + for(int i = 0; i < layers.len(); i++) + { + float lw, lh; + layers[i]->get_size(&lw, &lh); + *w = max(*w, lw); + *h = max(*h, lh); + } +} + + +int LAYERGROUP::swap_layers(int index0, int index1) +{ + if(index0 < 0 || index0 >= layers.len()) return index0; + if(index1 < 0 || index1 >= layers.len()) return index0; + if(index0 == index1) return index0; + swap(layers[index0], layers[index1]); + return index1; +} + +/******************************************************** + OTHER +*********************************************************/ +static 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(); +} + +// copied from gc_menu.cpp, should be more generalized +extern int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hidden=false); + +static vec4 get_button_color(const void *id, int checked) +{ + if(checked < 0) + return vec4(0,0,0,0.5f); + + if(checked > 0) + { + if(ui_hot_item() == id) + return vec4(1,0,0,0.75f); + return vec4(1,0,0,0.5f); + } + + if(ui_hot_item() == id) + return vec4(1,1,1,0.75f); + return vec4(1,1,1,0.5f); +} + +static void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; + ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 5.0f); + ui_do_label(r, text, 10, 0, -1); +} + +static void draw_editor_button_l(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; + ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 5.0f); + ui_do_label(r, text, 10, 0, -1); +} + +static void draw_editor_button_m(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; + ui_draw_rect(r, get_button_color(id, checked), 0, 5.0f); + ui_do_label(r, text, 10, 0, -1); +} + +static void draw_editor_button_r(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; + ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 5.0f); + ui_do_label(r, text, 10, 0, -1); +} + +static void draw_inc_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra; + ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 5.0f); + ui_do_label(r, ">", 10, 0, -1); +} + +static void draw_dec_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) +{ + if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra; + ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 5.0f); + ui_do_label(r, "<", 10, 0, -1); +} + + + +static void render_background(RECT view, int texture, float size, float brightness) +{ + gfx_texture_set(texture); + gfx_blend_normal(); + gfx_quads_begin(); + gfx_setcolor(brightness,brightness,brightness,1.0f); + gfx_quads_setsubset(0,0, view.w/size, view.h/size); + gfx_quads_drawTL(view.x, view.y, view.w, view.h); + gfx_quads_end(); +} + + +static int selected_layer = 0; +static int selected_group = 0; +static int selected_quad = -1; +int selected_points = 0; +static int selected_envelope = 0; + +static LAYERGROUP brush; +static LAYER_TILES tileset_picker(16, 16); + +static int ui_do_value_selector(void *id, RECT *r, const char *label, int current, int min, int max, float scale) +{ + /* logic */ + static float value; + int ret = 0; + int inside = ui_mouse_inside(r); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + { + if(inside) + ret = 1; + editor.lock_mouse = false; + ui_set_active_item(0); + } + else + { + if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + value += editor.mouse_delta_x*0.05f; + else + value += editor.mouse_delta_x; + + if(fabs(value) > scale) + { + int count = (int)(value/scale); + value = fmod(value, scale); + current += count; + if(current < min) + current = min; + if(current > max) + current = max; + } + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + { + editor.lock_mouse = true; + value = 0; + ui_set_active_item(id); + } + } + + if(inside) + ui_set_hot_item(id); + + // render + char buf[128]; + sprintf(buf, "%s %d", label, current); + ui_draw_rect(r, get_button_color(id, 0), CORNER_ALL, 5.0f); + ui_do_label(r, buf, 12, 0, -1); + return current; +} + + +LAYERGROUP *EDITOR::get_selected_group() +{ + if(selected_group >= 0 && selected_group < editor.map.groups.len()) + return editor.map.groups[selected_group]; + return 0x0; +} + +LAYER *EDITOR::get_selected_layer(int index) +{ + LAYERGROUP *group = get_selected_group(); + if(!group) + return 0x0; + + if(selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len()) + return group->layers[selected_layer]; + return 0x0; +} + +LAYER *EDITOR::get_selected_layer_type(int index, int type) +{ + LAYER *p = get_selected_layer(index); + if(p && p->type == type) + return p; + return 0x0; +} + +QUAD *EDITOR::get_selected_quad() +{ + LAYER_QUADS *ql = (LAYER_QUADS *)get_selected_layer_type(0, LAYERTYPE_QUADS); + if(!ql) + return 0; + if(selected_quad >= 0 && selected_quad < ql->quads.len()) + return &ql->quads[selected_quad]; + return 0; +} + + +static void do_toolbar(RECT toolbar) +{ + RECT button; + + ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + static int zoom_out_button = 0; + if(ui_do_button(&zoom_out_button, "ZO", 0, &button, draw_editor_button_l, "[NumPad-] Zoom out") || inp_key_down(KEY_KP_SUBTRACT)) + editor.zoom_level += 50; + + ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + static int zoom_normal_button = 0; + if(ui_do_button(&zoom_normal_button, "1:1", 0, &button, draw_editor_button_m, "[NumPad*] Zoom to normal and remove editor offset") || inp_key_down(KEY_KP_MULTIPLY)) + { + editor.editor_offset_x = 0; + editor.editor_offset_y = 0; + editor.zoom_level = 100; + } + + ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); + static int zoom_in_button = 0; + if(ui_do_button(&zoom_in_button, "ZI", 0, &button, draw_editor_button_r, "[NumPad+] Zoom in") || inp_key_down(KEY_KP_ADD)) + editor.zoom_level -= 50; + + if(editor.zoom_level < 50) + editor.zoom_level = 50; + editor.world_zoom = editor.zoom_level/100.0f; + + ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); + + + // brush manipulation + { + int enabled = brush.is_empty()?-1:0; + + // flip buttons + ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); + static int flipx_button = 0; + if(ui_do_button(&flipx_button, "^X", enabled, &button, draw_editor_button_l, "Flip brush horizontal")) + { + for(int i = 0; i < brush.layers.len(); i++) + brush.layers[i]->brush_flip_x(); + } + + ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); + static int flipy_button = 0; + if(ui_do_button(&flipy_button, "^Y", enabled, &button, draw_editor_button_r, "Flip brush vertical")) + { + for(int i = 0; i < brush.layers.len(); i++) + brush.layers[i]->brush_flip_y(); + } + } + + // quad manipulation + { + // do add button + ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); + ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); + static int new_button = 0; + + LAYER_QUADS *qlayer = (LAYER_QUADS *)editor.get_selected_layer_type(0, LAYERTYPE_QUADS); + LAYER_TILES *tlayer = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES); + if(ui_do_button(&new_button, "Add Quad", qlayer?0:-1, &button, draw_editor_button, "Adds a new quad")) + { + if(qlayer) + { + float mapping[4]; + LAYERGROUP *g = editor.get_selected_group(); + g->mapping(mapping); + int add_x = f2fx(mapping[0] + (mapping[2]-mapping[0])/2); + int add_y = f2fx(mapping[1] + (mapping[3]-mapping[1])/2); + + QUAD *q = qlayer->new_quad(); + for(int i = 0; i < 5; i++) + { + q->points[i].x += add_x; + q->points[i].y += add_y; + } + } + } + + ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); + ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); + static int sq_button = 0; + if(ui_do_button(&sq_button, "Sq. Quad", editor.get_selected_quad()?0:-1, &button, draw_editor_button, "Squares the current quad")) + { + QUAD *q = editor.get_selected_quad(); + if(q) + { + int top = q->points[0].y; + int left = q->points[0].x; + int bottom = q->points[0].y; + int right = q->points[0].x; + + for(int k = 1; k < 4; k++) + { + if(q->points[k].y < top) top = q->points[k].y; + if(q->points[k].x < left) left = q->points[k].x; + if(q->points[k].y > bottom) bottom = q->points[k].y; + if(q->points[k].x > right) right = q->points[k].x; + } + + q->points[0].x = left; q->points[0].y = top; + q->points[1].x = right; q->points[1].y = top; + q->points[2].x = left; q->points[2].y = bottom; + q->points[3].x = right; q->points[3].y = bottom; + } + } + + ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); + ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); + bool in_gamegroup = editor.game_group->layers.find(tlayer) != -1; + static int col_button = 0; + if(ui_do_button(&col_button, "Make Col", (in_gamegroup&&tlayer)?0:-1, &button, draw_editor_button, "Constructs collision from the current layer")) + { + LAYER_TILES *gl = editor.game_layer; + int w = min(gl->width, tlayer->width); + int h = min(gl->height, tlayer->height); + dbg_msg("", "w=%d h=%d", w, h); + 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 = tlayer->tiles[y*tlayer->width+x].index?TILE_SOLID:TILE_AIR; + } + } + } +} + +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); +} + +static void do_quad(QUAD *q, int index) +{ + enum + { + OP_NONE=0, + OP_MOVE_ALL, + OP_MOVE_PIVOT, + OP_ROTATE, + }; + + // some basic values + void *id = &q->points[4]; // use pivot addr as id + static POINT rotate_points[4]; + static float last_wx; + static float last_wy; + static int operation = OP_NONE; + static float rotate_angle = 0; + float wx = ui_mouse_world_x(); + float wy = ui_mouse_world_y(); + + // get pivot + float center_x = fx2f(q->points[4].x); + float center_y = fx2f(q->points[4].y); + + float dx = (center_x - wx); + float dy = (center_y - wy); + if(dx*dx+dy*dy < 10*10) + ui_set_hot_item(id); + + // draw selection background + if(selected_quad == index) + { + gfx_setcolor(0,0,0,1); + gfx_quads_draw(center_x, center_y, 7.0f, 7.0f); + } + + if(ui_active_item() == id) + { + // check if we only should move pivot + if(operation == OP_MOVE_PIVOT) + { + q->points[4].x += f2fx(wx-last_wx); + q->points[4].y += f2fx(wy-last_wy); + } + else if(operation == OP_MOVE_ALL) + { + // move all points including pivot + for(int v = 0; v < 5; v++) + { + q->points[v].x += f2fx(wx-last_wx); + q->points[v].y += f2fx(wy-last_wy); + } + } + else if(operation == OP_ROTATE) + { + for(int v = 0; v < 4; v++) + { + q->points[v] = rotate_points[v]; + rotate(&q->points[4], &q->points[v], rotate_angle); + } + } + + rotate_angle += (editor.mouse_delta_x) * 0.002f; + last_wx = wx; + last_wy = wy; + + if(!ui_mouse_button(0)) + { + editor.lock_mouse = false; + operation = OP_NONE; + ui_set_active_item(0); + } + + gfx_setcolor(1,1,1,1); + } + else if(ui_hot_item() == id) + { + gfx_setcolor(1,1,1,1); + editor.tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate"; + + if(ui_mouse_button(0)) + { + if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + operation = OP_MOVE_PIVOT; + else if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)) + { + editor.lock_mouse = true; + operation = OP_ROTATE; + rotate_angle = 0; + rotate_points[0] = q->points[0]; + rotate_points[1] = q->points[1]; + rotate_points[2] = q->points[2]; + rotate_points[3] = q->points[3]; + } + else + operation = OP_MOVE_ALL; + + ui_set_active_item(id); + selected_quad = index; + editor.props = PROPS_QUAD; + last_wx = wx; + last_wy = wy; + } + } + else + gfx_setcolor(0,1,0,1); + + gfx_quads_draw(center_x, center_y, 5.0f, 5.0f); +} + +static void do_quad_point(QUAD *q, int quad_index, int v) +{ + void *id = &q->points[v]; + + float wx = ui_mouse_world_x(); + float wy = ui_mouse_world_y(); + + float px = fx2f(q->points[v].x); + float py = fx2f(q->points[v].y); + + float dx = (px - wx); + float dy = (py - wy); + if(dx*dx+dy*dy < 10*10) + ui_set_hot_item(id); + + // draw selection background + if(selected_quad == quad_index && selected_points&(1< 0.5f) + moved = true; + } + + if(moved) + { + if(operation == OP_MOVEPOINT) + { + for(int m = 0; m < 4; m++) + if(selected_points&(1<points[m].x += f2fx(dx); + q->points[m].y += f2fx(dy); + } + } + else if(operation == OP_MOVEUV) + { + for(int m = 0; m < 4; m++) + if(selected_points&(1<texcoords[m].x += f2fx(dx*0.001f); + q->texcoords[m].y += f2fx(dy*0.001f); + } + } + } + + if(!ui_mouse_button(0)) + { + if(!moved) + { + if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + selected_points ^= 1<visible) + editor.map.groups[g]->render(); + } + + // render the game above everything else + if(editor.game_group->visible && editor.game_layer->visible) + { + editor.game_group->mapscreen(); + editor.game_layer->render(); + } + } + + static void *editor_id = (void *)&editor_id; + int inside = ui_mouse_inside(&view); + + // fetch mouse position + float wx = ui_mouse_world_x(); + float wy = ui_mouse_world_y(); + float mx = ui_mouse_x(); + float my = ui_mouse_y(); + + static float start_wx = 0; + static float start_wy = 0; + static float start_mx = 0; + static float start_my = 0; + + enum + { + OP_NONE=0, + OP_BRUSH_GRAB, + OP_BRUSH_DRAW, + OP_PAN_WORLD, + OP_PAN_EDITOR, + }; + + // remap the screen so it can display the whole tileset + if(show_picker) + { + RECT screen = *ui_screen(); + float size = 32.0*16.0f; + float w = size*(screen.w/view.w); + float h = size*(screen.h/view.h); + float x = -(view.x/screen.w)*w; + float y = -(view.y/screen.h)*h; + wx = x+w*mx/screen.w; + wy = y+h*my/screen.h; + gfx_mapscreen(x, y, x+w, y+h); + LAYER_TILES *t = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES); + if(t) + { + tileset_picker.image = t->image; + tileset_picker.tex_id = t->tex_id; + tileset_picker.render(); + } + } + + static int operation = OP_NONE; + + // draw layer borders + LAYER *edit_layers[16]; + int num_edit_layers = 0; + num_edit_layers = 0; + + if(show_picker) + { + edit_layers[0] = &tileset_picker; + num_edit_layers++; + } + else + { + edit_layers[0] = editor.get_selected_layer(0); + if(edit_layers[0]) + num_edit_layers++; + + LAYERGROUP *g = editor.get_selected_group(); + if(g) + { + g->mapscreen(); + + for(int i = 0; i < num_edit_layers; i++) + { + if(edit_layers[i]->type != LAYERTYPE_TILES) + continue; + + float w, h; + edit_layers[i]->get_size(&w, &h); + + gfx_texture_set(-1); + gfx_lines_begin(); + gfx_lines_draw(0,0, w,0); + gfx_lines_draw(w,0, w,h); + gfx_lines_draw(w,h, 0,h); + gfx_lines_draw(0,h, 0,0); + gfx_lines_end(); + } + } + } + + if(inside) + { + ui_set_hot_item(editor_id); + + // do global operations like pan and zoom + if(ui_active_item() == 0 && ui_mouse_button(0)) + { + start_wx = wx; + start_wy = wy; + start_mx = mx; + start_my = my; + + if(inp_key_pressed(KEY_LALT)) + { + if(inp_key_pressed(KEY_LSHIFT)) + operation = OP_PAN_EDITOR; + else + operation = OP_PAN_WORLD; + ui_set_active_item(editor_id); + } + } + + // brush editing + { + if(brush.is_empty()) + editor.tooltip = "Use left mouse button to drag and create a brush."; + else + editor.tooltip = "Use left mouse button to paint with the brush. Right button clears the brush."; + + if(ui_active_item() == editor_id) + { + RECT r; + r.x = start_wx; + r.y = start_wy; + r.w = wx-start_wx; + r.h = wy-start_wy; + if(r.w < 0) + { + r.x += r.w; + r.w = -r.w; + } + + if(r.h < 0) + { + r.y += r.h; + r.h = -r.h; + } + + if(operation == OP_BRUSH_DRAW) + { + if(!brush.is_empty()) + { + // draw with brush + for(int k = 0; k < num_edit_layers; k++) + { + if(edit_layers[k]->type == brush.layers[0]->type) + edit_layers[k]->brush_draw(brush.layers[0], wx, wy); + } + } + } + else if(operation == OP_BRUSH_GRAB) + { + if(!ui_mouse_button(0)) + { + // grab brush + dbg_msg("editor", "grabbing %f %f %f %f", r.x, r.y, r.w, r.h); + + // TODO: do all layers + int grabs = 0; + for(int k = 0; k < num_edit_layers; k++) + grabs += edit_layers[k]->brush_grab(&brush, r); + if(grabs == 0) + brush.clear(); + } + else + { + editor.map.groups[selected_group]->mapscreen(); + for(int k = 0; k < num_edit_layers; k++) + edit_layers[k]->brush_selecting(r); + gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + } + } + } + else + { + if(ui_mouse_button(1)) + brush.clear(); + + if(ui_mouse_button(0) && operation == OP_NONE) + { + ui_set_active_item(editor_id); + + if(brush.is_empty()) + operation = OP_BRUSH_GRAB; + else + { + operation = OP_BRUSH_DRAW; + for(int k = 0; k < num_edit_layers; k++) + { + if(edit_layers[k]->type == brush.layers[0]->type) + edit_layers[k]->brush_place(brush.layers[0], wx, wy); + } + + } + } + + if(!brush.is_empty()) + { + brush.offset_x = -(int)wx; + brush.offset_y = -(int)wy; + for(int i = 0; i < brush.layers.len(); i++) + { + if(brush.layers[i]->type == LAYERTYPE_TILES) + { + brush.offset_x = -(int)(wx/32.0f)*32; + brush.offset_y = -(int)(wy/32.0f)*32; + break; + } + } + + LAYERGROUP *g = editor.get_selected_group(); + brush.offset_x += g->offset_x; + brush.offset_y += g->offset_y; + brush.parallax_x = g->parallax_x; + brush.parallax_y = g->parallax_y; + brush.render(); + float w, h; + brush.get_size(&w, &h); + + gfx_texture_set(-1); + gfx_lines_begin(); + gfx_lines_draw(0,0, w,0); + gfx_lines_draw(w,0, w,h); + gfx_lines_draw(w,h, 0,h); + gfx_lines_draw(0,h, 0,0); + gfx_lines_end(); + + } + } + } + + if(!show_picker && brush.is_empty()) + { + // fetch layers + LAYERGROUP *g = editor.get_selected_group(); + if(g) + g->mapscreen(); + + for(int k = 0; k < num_edit_layers; k++) + { + if(edit_layers[k]->type == LAYERTYPE_QUADS) + { + LAYER_QUADS *layer = (LAYER_QUADS *)edit_layers[k]; + + gfx_texture_set(-1); + gfx_quads_begin(); + for(int i = 0; i < layer->quads.len(); i++) + { + for(int v = 0; v < 4; v++) + do_quad_point(&layer->quads[i], i, v); + + do_quad(&layer->quads[i], i); + } + gfx_quads_end(); + } + } + + gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + } + + // do panning + if(ui_active_item() == editor_id) + { + if(operation == OP_PAN_WORLD) + { + editor.world_offset_x -= editor.mouse_delta_x*editor.world_zoom; + editor.world_offset_y -= editor.mouse_delta_y*editor.world_zoom; + } + else if(operation == OP_PAN_EDITOR) + { + editor.editor_offset_x -= editor.mouse_delta_x*editor.world_zoom; + editor.editor_offset_y -= editor.mouse_delta_y*editor.world_zoom; + } + } + + + // release mouse + if(!ui_mouse_button(0)) + { + operation = OP_NONE; + ui_set_active_item(0); + } + + } + + // render screen sizes + if(editor.proof_borders) + { + LAYERGROUP *g = editor.game_group; + g->mapscreen(); + + gfx_texture_set(-1); + gfx_lines_begin(); + + float last_points[4]; + float start = 1.0f; //9.0f/16.0f; + float end = 16.0f/9.0f; + const int num_steps = 20; + for(int i = 0; i <= num_steps; i++) + { + float points[4]; + float aspect = start + (end-start)*(i/(float)num_steps); + + mapscreen_to_world( + editor.world_offset_x, editor.world_offset_y, + 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); + + if(i == 0) + { + gfx_lines_draw(points[0], points[1], points[2], points[1]); + gfx_lines_draw(points[0], points[3], points[2], points[3]); + } + + if(i != 0) + { + gfx_lines_draw(points[0], points[1], last_points[0], last_points[1]); + gfx_lines_draw(points[2], points[1], last_points[2], last_points[1]); + gfx_lines_draw(points[0], points[3], last_points[0], last_points[3]); + gfx_lines_draw(points[2], points[3], last_points[2], last_points[3]); + } + + if(i == num_steps) + { + gfx_lines_draw(points[0], points[1], points[0], points[3]); + gfx_lines_draw(points[2], points[1], points[2], points[3]); + } + + mem_copy(last_points, points, sizeof(points)); + } + + if(0) + { + gfx_setcolor(1.0f,0,0,1); + for(int i = 0; i < 4; i++) + { + float points[4]; + float aspects[] = {4.0f/3.0f, 5.0f/4.0f, 16.0f/10.0f, 16.0f/9.0f}; + float aspect = aspects[i]; + + mapscreen_to_world( + editor.world_offset_x, editor.world_offset_y, + 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); + + RECT r; + r.x = points[0]; + r.y = points[1]; + r.w = points[2]-points[0]; + r.h = points[3]-points[1]; + + gfx_lines_draw(r.x, r.y, r.x+r.w, r.y); + gfx_lines_draw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); + gfx_lines_draw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); + gfx_lines_draw(r.x, r.y+r.h, r.x, r.y); + } + } + + gfx_lines_end(); + } + + gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + ui_clip_disable(); +} + +int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val) +{ + int change = -1; + + for(int i = 0; props[i].name; i++) + { + RECT slot; + ui_hsplit_t(toolbox, 13.0f, &slot, toolbox); + RECT label, shifter; + ui_vsplit_mid(&slot, &label, &shifter); + ui_hmargin(&shifter, 1.0f, &shifter); + ui_do_label(&label, props[i].name, 10.0f, -1, -1); + + if(props[i].type == PROPTYPE_INT_STEP) + { + RECT inc, dec; + char buf[64]; + + ui_vsplit_r(&shifter, 10.0f, &shifter, &inc); + ui_vsplit_l(&shifter, 10.0f, &dec, &shifter); + sprintf(buf, "%d", props[i].value); + ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); + ui_do_label(&shifter, buf, 10.0f, 0, -1); + + if(ui_do_button(&ids[i], 0, 0, &dec, draw_dec_button, "Decrease")) + { + if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + *new_val = props[i].value-5; + else + *new_val = props[i].value-1; + change = i; + } + if(ui_do_button(((char *)&ids[i])+1, 0, 0, &inc, draw_inc_button, "Increase")) + { + if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + *new_val = props[i].value+5; + else + *new_val = props[i].value+1; + change = i; + } + } + else if(props[i].type == PROPTYPE_INT_SCROLL) + { + int new_value = ui_do_value_selector(&ids[i], &shifter, "", props[i].value, props[i].min, props[i].max, 1.0f); + if(new_value != props[i].value) + { + *new_val = new_value; + change = i; + } + } + else if(props[i].type == PROPTYPE_COLOR) + { + static const char *texts[4] = {"R", "G", "B", "A"}; + static int shift[] = {24, 16, 8, 0}; + int new_color = 0; + + for(int c = 0; c < 4; c++) + { + int v = (props[i].value >> shift[c])&0xff; + new_color |= ui_do_value_selector(((char *)&ids[i])+c, &shifter, texts[c], v, 0, 255, 1.0f)<= 0 && selected_group < editor.map.groups.len()) + valid_group = 1; + + if(valid_group && selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len()) + valid_layer = 1; + + int valid_group_mask = valid_group ? 0 : -1; + int valid_layer_mask = valid_layer ? 0 : -1; + + { + ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); + + // new layer group + ui_vsplit_l(&slot, 12.0f, &button, &slot); + static int new_layer_group_button = 0; + if(ui_do_button(&new_layer_group_button, "G+", 0, &button, draw_editor_button, "New group")) + { + editor.map.new_group(); + selected_group = editor.map.groups.len()-1; + editor.props = PROPS_GROUP; + } + + // new tile layer + ui_vsplit_l(&slot, 10.0f, &button, &slot); + ui_vsplit_l(&slot, 12.0f, &button, &slot); + static int new_tile_layer_button = 0; + if(ui_do_button(&new_tile_layer_button, "T+", valid_group_mask, &button, draw_editor_button, "New tile layer")) + { + LAYER *l = new LAYER_TILES(50, 50); + editor.map.groups[selected_group]->add_layer(l); + selected_layer = editor.map.groups[selected_group]->layers.len()-1; + editor.props = PROPS_LAYER; + } + + // new quad layer + ui_vsplit_l(&slot, 2.0f, &button, &slot); + ui_vsplit_l(&slot, 12.0f, &button, &slot); + static int new_quad_layer_button = 0; + if(ui_do_button(&new_quad_layer_button, "Q+", valid_group_mask, &button, draw_editor_button, "New quad layer")) + { + LAYER *l = new LAYER_QUADS; + editor.map.groups[selected_group]->add_layer(l); + selected_layer = editor.map.groups[selected_group]->layers.len()-1; + editor.props = PROPS_LAYER; + } + + // remove layer + ui_vsplit_r(&slot, 12.0f, &slot, &button); + static int delete_layer_button = 0; + if(ui_do_button(&delete_layer_button, "L-", valid_layer_mask, &button, draw_editor_button, "Delete layer")) + editor.map.groups[selected_group]->delete_layer(selected_layer); + + // remove group + ui_vsplit_r(&slot, 2.0f, &slot, &button); + ui_vsplit_r(&slot, 12.0f, &slot, &button); + static int delete_group_button = 0; + if(ui_do_button(&delete_group_button, "G-", valid_group_mask, &button, draw_editor_button, "Delete group")) + editor.map.delete_group(selected_group); + } + + ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox); + + // render layers + { + for(int g = 0; g < editor.map.groups.len(); g++) + { + RECT visible_toggle; + ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); + ui_vsplit_l(&slot, 12, &visible_toggle, &slot); + if(ui_do_button(&editor.map.groups[g]->visible, editor.map.groups[g]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, "Toggle group visibility")) + editor.map.groups[g]->visible = !editor.map.groups[g]->visible; + + sprintf(buf, "#%d %s", g, editor.map.groups[g]->name); + if(ui_do_button(&editor.map.groups[g], buf, g==selected_group, &slot, draw_editor_button_r, "Select group")) + { + selected_group = g; + selected_layer = 0; + editor.props = PROPS_GROUP; + } + + ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox); + + for(int i = 0; i < editor.map.groups[g]->layers.len(); i++) + { + //visible + ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); + ui_vsplit_l(&slot, 12.0f, 0, &button); + ui_vsplit_l(&button, 15, &visible_toggle, &button); + + if(ui_do_button(&editor.map.groups[g]->layers[i]->visible, editor.map.groups[g]->layers[i]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, "Toggle layer visibility")) + editor.map.groups[g]->layers[i]->visible = !editor.map.groups[g]->layers[i]->visible; + + sprintf(buf, "#%d %s ", i, editor.map.groups[g]->layers[i]->type_name); + if(ui_do_button(editor.map.groups[g]->layers[i], buf, g==selected_group&&i==selected_layer, &button, draw_editor_button_r, "Select layer")) + { + selected_layer = i; + selected_group = g; + editor.props = PROPS_LAYER; + } + ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox); + } + ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox); + } + } + + propsbox = layersbox; + + // group properties + if(editor.props == PROPS_GROUP && valid_group) + { + ui_hsplit_t(&propsbox, 12.0f, &slot, &propsbox); + ui_do_label(&slot, "Group Props", 12.0f, -1, -1); + + enum + { + PROP_ORDER=0, + PROP_POS_X, + PROP_POS_Y, + PROP_PARA_X, + PROP_PARA_Y, + NUM_PROPS, + }; + + PROPERTY props[] = { + {"Order", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1}, + {"Pos X", -editor.map.groups[selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Pos Y", -editor.map.groups[selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para X", editor.map.groups[selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, + {"Para Y", editor.map.groups[selected_group]->parallax_y, 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(&propsbox, props, ids, &new_val); + if(prop == PROP_ORDER) + selected_group = editor.map.swap_groups(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[selected_group]->parallax_x = new_val; + else if(prop == PROP_PARA_Y) + editor.map.groups[selected_group]->parallax_y = new_val; + else if(prop == PROP_POS_X) + editor.map.groups[selected_group]->offset_x = -new_val; + else if(prop == PROP_POS_Y) + editor.map.groups[selected_group]->offset_y = -new_val; + } + } + + // layer properties + if(editor.get_selected_layer(0)) + { + LAYERGROUP *current_group = editor.map.groups[selected_group]; + LAYER *current_layer = editor.get_selected_layer(0); + + if(editor.props == PROPS_LAYER) + { + ui_hsplit_t(&propsbox, 15.0f, &slot, &propsbox); + ui_do_label(&slot, "Layer Props", 12.0f, -1, -1); + + enum + { + PROP_GROUP=0, + PROP_ORDER, + NUM_PROPS, + }; + + PROPERTY props[] = { + {"Group", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1}, + {"Order", selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()}, + {0}, + }; + + static int ids[NUM_PROPS] = {0}; + int new_val = 0; + int prop = editor.do_properties(&propsbox, props, ids, &new_val); + + if(prop == PROP_ORDER) + selected_layer = current_group->swap_layers(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); + selected_group = new_val; + selected_layer = editor.map.groups[new_val]->layers.len()-1; + } + } + } + + current_layer->render_properties(&propsbox); + } +} + +static void render_images(RECT toolbox, RECT view) +{ + static int selected_image = 0; + + for(int i = 0; i < editor.map.images.len(); i++) + { + char buf[128]; + sprintf(buf, "#%d %dx%d", i, editor.map.images[i]->width, editor.map.images[i]->height); + RECT slot; + ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); + + if(ui_do_button(&editor.map.images[i], buf, selected_image == i, &slot, draw_editor_button, "Select image")) + selected_image = i; + + ui_hsplit_t(&toolbox, 2.0f, 0, &toolbox); + + // render image + if(selected_image == i) + { + RECT r; + ui_margin(&view, 10.0f, &r); + if(r.h < r.w) + r.w = r.h; + else + r.h = r.w; + gfx_texture_set(editor.map.images[i]->tex_id); + gfx_blend_normal(); + gfx_quads_begin(); + gfx_quads_drawTL(r.x, r.y, r.w, r.h); + gfx_quads_end(); + + } + } + + RECT slot; + ui_hsplit_t(&toolbox, 5.0f, &slot, &toolbox); + ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); + + // new image + static int new_image_button = 0; + if(ui_do_button(&new_image_button, "(Load New Image)", 0, &slot, draw_editor_button, "Load a new image to use in the map")) + editor.dialog = DIALOG_LOAD_IMAGE; + + ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); +} + +static void editor_listdir_callback(const char *name, int is_dir, void *user) +{ + if(name[0] == '.') // skip this shit! + return; + + RECT *view = (RECT *)user; + RECT button; + ui_hsplit_t(view, 15.0f, &button, view); + ui_hsplit_t(view, 2.0f, 0, view); + + if(ui_do_button((void*)(10+(int)button.y), name, 0, &button, draw_editor_button, 0)) + { + char buf[512]; + sprintf(buf, "tilesets/%s", name); + + IMAGE imginfo; + if(!gfx_load_png(&imginfo, buf)) + return; + + IMAGE *img = new IMAGE; + *img = imginfo; + img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO); + editor.map.images.add(img); + + //tilesets_set_img(tilesets_new(), img.width, img.height, img.data); + editor.dialog = DIALOG_NONE; + } +} + +static void render_dialog_load_image() +{ + // GUI coordsys + gfx_clear(0.25f,0.25f,0.25f); + + gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + + RECT view = *ui_screen(); + fs_listdir("tilesets", editor_listdir_callback, &view); + + if(inp_key_pressed(KEY_ESC)) + editor.dialog = DIALOG_NONE; +} + +static void render_modebar(RECT view) +{ + RECT button; + + // mode buttons + { + ui_vsplit_l(&view, 40.0f, &button, &view); + static int map_button = 0; + if(ui_do_button(&map_button, "Map", editor.mode == MODE_MAP, &button, draw_editor_button_l, "Switch to edit global map settings")) + editor.mode = MODE_MAP; + + ui_vsplit_l(&view, 40.0f, &button, &view); + static int tile_button = 0; + if(ui_do_button(&tile_button, "Layers", editor.mode == MODE_LAYERS, &button, draw_editor_button_m, "Switch to edit layers")) + editor.mode = MODE_LAYERS; + + ui_vsplit_l(&view, 40.0f, &button, &view); + static int img_button = 0; + if(ui_do_button(&img_button, "Images", editor.mode == MODE_IMAGES, &button, draw_editor_button_r, "Switch to manage images")) + editor.mode = MODE_IMAGES; + } + + ui_vsplit_l(&view, 5.0f, 0, &view); + + // animate button + ui_vsplit_l(&view, 30.0f, &button, &view); + static int animate_button = 0; + if(ui_do_button(&animate_button, "Anim", editor.animate, &button, draw_editor_button, "[ctrl+m] Toggle animation") || + (inp_key_down('M') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) + { + editor.animate_start = time_get(); + editor.animate = !editor.animate; + } + + ui_vsplit_l(&view, 5.0f, 0, &view); + + // proof button + ui_vsplit_l(&view, 30.0f, &button, &view); + static int proof_button = 0; + if(ui_do_button(&proof_button, "Proof", editor.proof_borders, &button, draw_editor_button, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") || + (inp_key_down('P') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) + { + editor.proof_borders = !editor.proof_borders; + } + + // spacing + //ui_vsplit_l(&view, 10.0f, 0, &view); +} + +static void render_statusbar(RECT view) +{ + RECT button; + ui_vsplit_r(&view, 60.0f, &view, &button); + static int envelope_button = 0; + if(ui_do_button(&envelope_button, "Envelopes", editor.show_envelope_editor, &button, draw_editor_button, "Toggles the envelope editor")) + editor.show_envelope_editor = (editor.show_envelope_editor+1)%4; + + if(editor.tooltip) + ui_do_label(&view, editor.tooltip, 12.0f, -1, -1); +} + +static void render_envelopeeditor(RECT view) +{ + if(selected_envelope < 0) selected_envelope = 0; + if(selected_envelope >= editor.map.envelopes.len()) selected_envelope--; + + ENVELOPE *envelope = 0; + if(selected_envelope >= 0 && selected_envelope < editor.map.envelopes.len()) + envelope = editor.map.envelopes[selected_envelope]; + + bool show_colorbar = false; + if(envelope && envelope->channels == 4) + show_colorbar = true; + + RECT toolbar, curvebar, colorbar; + ui_hsplit_t(&view, 20.0f, &toolbar, &view); + ui_hsplit_t(&view, 20.0f, &curvebar, &view); + ui_margin(&toolbar, 2.0f, &toolbar); + ui_margin(&curvebar, 2.0f, &curvebar); + + if(show_colorbar) + { + ui_hsplit_t(&view, 20.0f, &colorbar, &view); + ui_margin(&colorbar, 2.0f, &colorbar); + render_background(colorbar, checker_texture, 16.0f, 1.0f); + } + + render_background(view, checker_texture, 32.0f, 0.1f); + + // do the toolbar + { + RECT button; + ENVELOPE *new_env = 0; + + ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button); + static int new_4d_button = 0; + if(ui_do_button(&new_4d_button, "Color+", 0, &button, draw_editor_button, "Creates a new color envelope")) + new_env = editor.map.new_envelope(4); + + ui_vsplit_r(&toolbar, 5.0f, &toolbar, &button); + ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button); + static int new_2d_button = 0; + if(ui_do_button(&new_2d_button, "Pos.+", 0, &button, draw_editor_button, "Creates a new pos envelope")) + new_env = editor.map.new_envelope(3); + + if(new_env) // add the default points + { + if(new_env->channels == 4) + { + new_env->add_point(0, 1,1,1,1); + new_env->add_point(1000, 1,1,1,1); + } + else + { + new_env->add_point(0, 0); + new_env->add_point(1000, 0); + } + } + + RECT shifter, inc, dec; + ui_vsplit_l(&toolbar, 60.0f, &shifter, &toolbar); + ui_vsplit_r(&shifter, 15.0f, &shifter, &inc); + ui_vsplit_l(&shifter, 15.0f, &dec, &shifter); + char buf[512]; + sprintf(buf, "%d/%d", selected_envelope+1, editor.map.envelopes.len()); + ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); + ui_do_label(&shifter, buf, 14.0f, 0, -1); + + static int prev_button = 0; + if(ui_do_button(&prev_button, 0, 0, &dec, draw_dec_button, "Previous Envelope")) + selected_envelope--; + + static int next_button = 0; + if(ui_do_button(&next_button, 0, 0, &inc, draw_inc_button, "Next Envelope")) + selected_envelope++; + + if(envelope) + { + ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); + ui_vsplit_l(&toolbar, 35.0f, &button, &toolbar); + ui_do_label(&button, "Name:", 14.0f, -1, -1); + + ui_vsplit_l(&toolbar, 80.0f, &button, &toolbar); + static int name_box = 0; + ui_do_edit_box(&name_box, &button, envelope->name, sizeof(envelope->name)); + } + } + + if(envelope) + { + static array selection; + static int envelope_editor_id = 0; + static int active_channels = 0xf; + + if(envelope) + { + RECT button; + + ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); + + static const char *names[4][4] = { + {"X", "", "", ""}, + {"X", "Y", "", ""}, + {"X", "Y", "R", ""}, + {"R", "G", "B", "A"}, + }; + + static int channel_buttons[4] = {0}; + int bit = 1; + ui_draw_button_func draw_func; + + for(int i = 0; i < envelope->channels; i++, bit<<=1) + { + ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); + + if(i == 0) draw_func = draw_editor_button_l; + else if(i == envelope->channels-1) draw_func = draw_editor_button_r; + else draw_func = draw_editor_button_m; + + if(ui_do_button(&channel_buttons[i], names[envelope->channels-1][i], active_channels&bit, &button, draw_func, 0)) + active_channels ^= bit; + } + } + + float end_time = envelope->end_time(); + if(end_time < 1) + end_time = 1; + + envelope->find_top_bottom(); + float top = envelope->top; + float bottom = envelope->bottom; + + if(top < 1) + top = 1; + if(bottom >= 0) + bottom = 0; + + float timescale = end_time/view.w; + float valuescale = (top-bottom)/view.h; + + if(ui_mouse_inside(&view)) + ui_set_hot_item(&envelope_editor_id); + + if(ui_hot_item() == &envelope_editor_id) + { + // do stuff + if(envelope) + { + if(ui_mouse_button_clicked(1)) + { + // add point + int time = (int)(((ui_mouse_x()-view.x)*timescale)*1000.0f); + //float env_y = (ui_mouse_y()-view.y)/timescale; + envelope->add_point(time, + f2fx(envelope->eval(time, 0)), + f2fx(envelope->eval(time, 1)), + f2fx(envelope->eval(time, 2)), + f2fx(envelope->eval(time, 3))); + } + + editor.tooltip = "Press right mouse button to create a new point"; + } + } + + vec3 colors[] = {vec3(1,0.2f,0.2f), vec3(0.2f,1,0.2f), vec3(0.2f,0.2f,1), vec3(1,1,0.2f)}; + + // render lines + { + gfx_texture_set(-1); + gfx_lines_begin(); + for(int c = 0; c < envelope->channels; c++) + { + if(active_channels&(1<eval(0.000001f, c); + int steps = (int)((view.w/ui_screen()->w) * gfx_screenwidth()); + for(int i = 1; i <= steps; i++) + { + float a = i/(float)steps; + float v = envelope->eval(a*end_time, c); + v = (v-bottom)/(top-bottom); + + gfx_lines_draw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h); + prev_x = a; + prev_value = v; + } + } + gfx_lines_end(); + } + + // render curve options + { + for(int i = 0; i < envelope->points.len()-1; i++) + { + float t0 = envelope->points[i].time/1000.0f/end_time; + float t1 = envelope->points[i+1].time/1000.0f/end_time; + + //dbg_msg("", "%f", end_time); + + RECT v; + v.x = curvebar.x + (t0+(t1-t0)*0.5f) * curvebar.w; + v.y = curvebar.y; + v.h = curvebar.h; + v.w = curvebar.h; + v.x -= v.w/2; + void *id = &envelope->points[i].curvetype; + const char *type_name[] = { + "N", "L", "S", "F", "M" + }; + + if(ui_do_button(id, type_name[envelope->points[i].curvetype], 0, &v, draw_editor_button, "Switch curve type")) + envelope->points[i].curvetype = (envelope->points[i].curvetype+1)%NUM_CURVETYPES; + } + } + + // render colorbar + if(show_colorbar) + { + gfx_texture_set(-1); + gfx_quads_begin(); + for(int i = 0; i < envelope->points.len()-1; i++) + { + float r0 = fx2f(envelope->points[i].values[0]); + float g0 = fx2f(envelope->points[i].values[1]); + float b0 = fx2f(envelope->points[i].values[2]); + float a0 = fx2f(envelope->points[i].values[3]); + float r1 = fx2f(envelope->points[i+1].values[0]); + float g1 = fx2f(envelope->points[i+1].values[1]); + float b1 = fx2f(envelope->points[i+1].values[2]); + float a1 = fx2f(envelope->points[i+1].values[3]); + + gfx_setcolorvertex(0, r0, g0, b0, a0); + gfx_setcolorvertex(1, r1, g1, b1, a1); + gfx_setcolorvertex(2, r1, g1, b1, a1); + gfx_setcolorvertex(3, r0, g0, b0, a0); + + float x0 = envelope->points[i].time/1000.0f/end_time; +// float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); + float x1 = envelope->points[i+1].time/1000.0f/end_time; + //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom); + RECT v; + v.x = colorbar.x + x0*colorbar.w; + v.y = colorbar.y; + v.w = (x1-x0)*colorbar.w; + v.h = colorbar.h; + + gfx_quads_drawTL(v.x, v.y, v.w, v.h); + } + gfx_quads_end(); + } + + // render handles + { + static bool move = false; + + int current_value = 0, current_time = 0; + + gfx_texture_set(-1); + gfx_quads_begin(); + for(int c = 0; c < envelope->channels; c++) + { + if(!(active_channels&(1<points.len(); i++) + { + float x0 = envelope->points[i].time/1000.0f/end_time; + float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); + RECT final; + final.x = view.x + x0*view.w; + final.y = view.y+view.h - y0*view.h; + final.x -= 2.0f; + final.y -= 2.0f; + final.w = 4.0f; + final.h = 4.0f; + + void *id = &envelope->points[i].values[c]; + + if(ui_mouse_inside(&final)) + ui_set_hot_item(id); + + float colormod = 1.0f; + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + { + ui_set_active_item(0); + move = false; + } + else + { + envelope->points[i].values[c] -= f2fx(editor.mouse_delta_y*valuescale); + if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) + { + if(i != 0) + { + envelope->points[i].time += (int)((editor.mouse_delta_x*timescale)*1000.0f); + if(envelope->points[i].time < envelope->points[i-1].time) + envelope->points[i].time = envelope->points[i-1].time + 1; + if(i+1 != envelope->points.len() && envelope->points[i].time > envelope->points[i+1].time) + envelope->points[i].time = envelope->points[i+1].time - 1; + } + } + } + + colormod = 100.0f; + gfx_setcolor(1,1,1,1); + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + { + selection.clear(); + selection.add(i); + ui_set_active_item(id); + } + + // remove point + if(ui_mouse_button_clicked(1)) + envelope->points.removebyindex(i); + + colormod = 100.0f; + gfx_setcolor(1,0.75f,0.75f,1); + editor.tooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."; + } + + if(ui_active_item() == id || ui_hot_item() == id) + { + current_time = envelope->points[i].time; + current_value = envelope->points[i].values[c]; + } + + gfx_setcolor(colors[c].r*colormod, colors[c].g*colormod, colors[c].b*colormod, 1.0f); + gfx_quads_drawTL(final.x, final.y, final.w, final.h); + } + } + gfx_quads_end(); + + char buf[512]; + sprintf(buf, "%.3f %.3f", current_time/1000.0f, fx2f(current_value)); + ui_do_label(&toolbar, buf, 14.0f, 0, -1); + } + } +} + +static void editor_render() +{ + // basic start + gfx_clear(1.0f,0.0f,1.0f); + RECT view = *ui_screen(); + gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + + // reset tip + editor.tooltip = 0; + + // render checker + render_background(view, checker_texture, 32.0f, 1.0f); + + RECT modebar, toolbar, statusbar, envelope_editor, propsbar; + + if(editor.gui_active) + { + + ui_hsplit_t(&view, 16.0f, &toolbar, &view); + ui_vsplit_l(&view, 80.0f, &propsbar, &view); + ui_hsplit_b(&view, 16.0f, &view, &statusbar); + + + float brightness = 0.25f; + + render_background(propsbar, background_texture, 128.0f, brightness); + ui_margin(&propsbar, 2.0f, &propsbar); + + render_background(toolbar, background_texture, 128.0f, brightness); + ui_margin(&toolbar, 2.0f, &toolbar); + ui_vsplit_l(&toolbar, 220.0f, &modebar, &toolbar); + + render_background(statusbar, background_texture, 128.0f, brightness); + ui_margin(&statusbar, 2.0f, &statusbar); + + if(editor.show_envelope_editor) + { + float size = 125.0f; + if(editor.show_envelope_editor == 2) + size *= 2.0f; + else if(editor.show_envelope_editor == 3) + size *= 3.0f; + ui_hsplit_b(&view, size, &view, &envelope_editor); + render_background(envelope_editor, background_texture, 128.0f, brightness); + ui_margin(&envelope_editor, 2.0f, &envelope_editor); + } + } + + if(editor.dialog == DIALOG_LOAD_IMAGE) + render_dialog_load_image(); + else if(editor.mode == MODE_LAYERS) + render_layers(propsbar, toolbar, view); + else if(editor.mode == MODE_IMAGES) + render_images(propsbar, view); + + gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); + + if(editor.gui_active) + { + render_modebar(modebar); + if(editor.show_envelope_editor) + render_envelopeeditor(envelope_editor); + render_statusbar(statusbar); + } + + //do_propsdialog(); + + // render butt ugly mouse cursor + float mx = ui_mouse_x(); + float my = ui_mouse_y(); + gfx_texture_set(cursor_texture); + gfx_quads_begin(); + gfx_quads_drawTL(mx,my, 16.0f, 16.0f); + gfx_quads_end(); +} + +void editor_reset(bool create_default=true) +{ + editor.map.groups.deleteall(); + editor.map.envelopes.deleteall(); + editor.map.images.deleteall(); + + editor.game_layer = 0; + editor.game_group = 0; + + // create default layers + if(create_default) + { + editor.make_game_group(editor.map.new_group()); + editor.make_game_layer(new LAYER_GAME(50, 50)); + editor.game_group->add_layer(editor.game_layer); + } +} + +void EDITOR::make_game_layer(LAYER *layer) +{ + editor.game_layer = (LAYER_GAME *)layer; + editor.game_layer->tex_id = entities_texture; + editor.game_layer->readonly = true; +} + +void EDITOR::make_game_group(LAYERGROUP *group) +{ + editor.game_group = group; + editor.game_group->game_group = true; + editor.game_group->name = "Game"; +} + +template +static int make_version(int i, const T &v) +{ return (i<<16)+sizeof(T); } + +enum +{ + MAPITEMTYPE_VERSION=0, + MAPITEMTYPE_INFO, + MAPITEMTYPE_IMAGE, + MAPITEMTYPE_ENVELOPE, + MAPITEMTYPE_GROUP, + MAPITEMTYPE_LAYER, +}; + + +void editor_load_old(DATAFILE *df) +{ + // 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 < editor.map.groups[0]->layers.len()-1; i++) + { + if(editor.map.groups[0]->layers[i] == editor.game_layer) + editor.map.groups[0]->swap_layers(i, i+1); + } + + game_width = tmap->width; + game_height = tmap->height; + } + + // add new layer + editor.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); + + IMAGE *img = new 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); + editor.map.images.add(img); + + // unload image + datafile_unload_data(df, imgres->image_data); + } + } + + // load entities + { + LAYER_GAME *g = editor.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_ROCKET; + 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_HEATH_1; + } + + if(id > 0 && x >= 0 && x < g->width && y >= 0 && y < g->height) + g->tiles[y*g->width+x].index = id; + } + } + } +} + +int EDITOR::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 < map.images.len(); i++) + { + IMAGE *img = map.images[i]; + MAPITEM_IMAGE item; + item.version = 1; + + item.width = img->width; + item.height = img->height; + item.external = 0; + item.image_name = -1; + 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 < map.groups.len(); g++) + { + LAYERGROUP *group = map.groups[g]; + MAPITEM_GROUP gitem; + gitem.version = 1; + + 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.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]; + MAPITEM_LAYER_TILEMAP item; + item.version = 2; + + item.layer.flags = 0; + 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 = 0; + 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); + } + + // finish the data file + datafile_finish(df); + dbg_msg("editor", "done"); + return 1; +} + + +int EDITOR::load(const char *filename) +{ + DATAFILE *df = datafile_load(filename); + if(!df) + return 0; + + // check version + MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0); + if(!item) + { + // import old map + editor_reset(); + editor_load_old(df); + } + 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); + void *data = datafile_get_data(df, item->image_data); + + IMAGE *img = new IMAGE; + img->width = item->width; + img->height = item->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); + editor.map.images.add(img); + + // unload image + datafile_unload_data(df, item->image_data); + } + } + + // 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); + LAYERGROUP *group = map.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; + + for(int l = 0; l < gitem->num_layers; l++) + { + 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); + editor.make_game_layer(tiles); + make_game_group(group); + } + else + tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height); + + 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 *layer = new LAYER_QUADS; + layer->image = quads_item->image; + if(layer->image < -1 || layer->image >= map.images.len()) + layer->image = -1; + void *data = datafile_get_data_swapped(df, quads_item->data); + group->add_layer(layer); + layer->quads.setsize(quads_item->num_quads); + mem_copy(layer->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads); + datafile_unload_data(df, quads_item->data); + } + } + } + } + } + + datafile_unload(df); + + return 0; +} + + +extern "C" void editor_init() +{ + checker_texture = gfx_load_texture("data/editor/checker.png", IMG_AUTO); + background_texture = gfx_load_texture("data/editor/background.png", IMG_AUTO); + cursor_texture = gfx_load_texture("data/editor/cursor.png", IMG_AUTO); + entities_texture = gfx_load_texture("data/editor/entities.png", IMG_AUTO); + + tileset_picker.make_palette(); + tileset_picker.readonly = true; + + editor_reset(); + //editor.load("debug_test.map"); + +#if 0 + IMAGE *img = new IMAGE; + gfx_load_png(img, "tilesets/grassland_main.png"); + img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data); + editor.map.images.add(img); + + + ENVELOPE *e = editor.map.new_envelope(4); + e->add_point(0, 0, 0); + e->add_point(1000, f2fx(1), f2fx(0.75f)); + e->add_point(2000, f2fx(0.75f), f2fx(1)); + e->add_point(3000, 0, 0); + + editor.animate = true; + editor.animate_start = time_get(); + + editor.show_envelope_editor = 1; +#endif + +/* + if(1) + { + float w, h; + float amount = 1300*1000; + float max = 1500; + dbg_msg("", "%f %f %f %f", (900*(5/4.0f))*900.0f, (900*(4/3.0f))*900.0f, (900*(16/9.0f))*900.0f, (900*(16/10.0f))*900.0f); + dbg_msg("", "%f", 900*(16/9.0f)); + calc_screen_params(amount, max, max, 5.0f/4.0f, &w, &h); dbg_msg("", "5:4 %f %f %f", w, h, w*h); + calc_screen_params(amount, max, max, 4.0f/3.0f, &w, &h); dbg_msg("", "4:3 %f %f %f", w, h, w*h); + calc_screen_params(amount, max, max, 16.0f/9.0f, &w, &h); dbg_msg("", "16:9 %f %f %f", w, h, w*h); + calc_screen_params(amount, max, max, 16.0f/10.0f, &w, &h); dbg_msg("", "16:10 %f %f %f", w, h, w*h); + + calc_screen_params(amount, max, max, 9.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h); + calc_screen_params(amount, max, max, 16.0f/3.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h); + calc_screen_params(amount, max, max, 3.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h); + }*/ +} + +extern "C" void editor_update_and_render() +{ + static int mouse_x = 0; + static int mouse_y = 0; + + editor.animate_time = (time_get()-editor.animate_start)/(float)time_freq(); + + // handle mouse movement + float mx, my, mwx, mwy; + int rx, ry; + { + inp_mouse_relative(&rx, &ry); + editor.mouse_delta_x = rx; + editor.mouse_delta_y = ry; + + if(!editor.lock_mouse) + { + mouse_x += rx; + mouse_y += ry; + } + + if(mouse_x < 0) mouse_x = 0; + if(mouse_y < 0) mouse_y = 0; + if(mouse_x > ui_screen()->w) mouse_x = (int)ui_screen()->w; + if(mouse_y > ui_screen()->h) mouse_y = (int)ui_screen()->h; + + // update the ui + mx = mouse_x; + my = mouse_y; + mwx = 0; + mwy = 0; + + // fix correct world x and y + LAYERGROUP *g = editor.get_selected_group(); + if(g) + { + float points[4]; + g->mapping(points); + + float world_width = points[2]-points[0]; + float world_height = points[3]-points[1]; + + mwx = points[0] + world_width * (mouse_x/ui_screen()->w); + mwy = points[1] + world_height * (mouse_y/ui_screen()->h); + editor.mouse_delta_wx = editor.mouse_delta_x*(world_width / ui_screen()->w); + editor.mouse_delta_wy = editor.mouse_delta_y*(world_height / ui_screen()->h); + } + + int buttons = 0; + if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; + if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; + if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; + + ui_update(mx,my,mwx,mwy,buttons); + } + + // toggle gui + if(inp_key_down(KEY_TAB)) + editor.gui_active = !editor.gui_active; + + if(inp_key_down(KEY_F5)) + editor.save("data/maps/debug_test2.map"); + + if(inp_key_down(KEY_F8)) + editor.load("data/maps/debug_test.map"); + + editor_render(); + inp_clear_events(); +} + diff --git a/src/game/editor/ed_editor.hpp b/src/game/editor/ed_editor.hpp new file mode 100644 index 00000000..15ba1dad --- /dev/null +++ b/src/game/editor/ed_editor.hpp @@ -0,0 +1,474 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include "array.h" +#include "../g_mapitems.h" + +extern "C" { + #include + #include + #include + #include +} + +#include + +// EDITOR SPECIFIC +template +void swap(T &a, T &b) +{ + T tmp = a; + a = b; + b = tmp; +} + +enum +{ + MODE_MAP=0, + MODE_LAYERS, + MODE_IMAGES, + + DIALOG_NONE=0, + DIALOG_LOAD_IMAGE, +}; + +typedef struct // as in file +{ + POINT position; + int type; +} ENTITY; + +enum +{ + CURVETYPE_STEP=0, + CURVETYPE_LINEAR, + CURVETYPE_SLOW, + CURVETYPE_FAST, + CURVETYPE_SMOOTH, + NUM_CURVETYPES, + +}; + +typedef struct // as in file +{ + int time; // in ms + int curvetype; + int values[4]; // 1-4 depending on envelope (22.10 fixed point) +} ENVPOINT; + +class ENVELOPE +{ +public: + int channels; + array 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(); + } + + void find_top_bottom() + { + top = -1000000000.0f; + bottom = 1000000000.0f; + for(int i = 0; i < points.len(); i++) + { + for(int c = 0; c < channels; c++) + { + float v = fx2f(points[i].values[c]); + if(v > top) top = v; + if(v < bottom) bottom = v; + } + } + } + + float eval(float time, int channel) + { + if(channel >= channels) + return 0; + if(points.len() == 0) + return 0; + if(points.len() == 1) + return points[0].values[channel]; + + time = fmod(time, end_time())*1000.0f; + for(int i = 0; i < points.len() - 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; + + float v0 = fx2f(points[i].values[channel]); + float v1 = fx2f(points[i+1].values[channel]); + + 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 + } + + return v0 + (v1-v0) * a; + } + } + + return points[points.len()-1].values[channel]; + } + + 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; + } + + 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 render() {} + virtual void render_properties(RECT *toolbox) {} + + virtual void get_size(float *w, float *h) { *w = 0; *h = 0;} + + const char *type_name; + int type; + + bool readonly; + bool visible; +}; + +class LAYERGROUP +{ +public: + array layers; + + int offset_x; + int offset_y; + + int parallax_x; + int parallax_y; + + 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); +}; + +class IMAGE : public IMAGE_INFO +{ +public: + IMAGE() + { + tex_id = -1; + name[0] = 0; + } + + int tex_id; + char name[128]; +}; + +class MAP +{ +public: + array groups; + array images; + array envelopes; + + 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); + } +}; + + +struct PROPERTY +{ + const char *name; + int value; + int type; + int min; + int max; +}; + +enum +{ + PROPTYPE_NULL=0, + PROPTYPE_INT_STEP, + PROPTYPE_INT_SCROLL, + PROPTYPE_COLOR, + + PROPS_NONE=0, + PROPS_GROUP, + PROPS_LAYER, + PROPS_QUAD, + PROPS_QUAD_POINT, +}; + +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; + mouse_delta_x = 0; + mouse_delta_y = 0; + mouse_delta_wx = 0; + mouse_delta_wy = 0; + + gui_active = true; + proof_borders = false; + + + animate = false; + animate_start = 0; + animate_time = 0; + + props = PROPS_NONE; + + show_envelope_editor = 0; + } + + void make_game_group(LAYERGROUP *group); + void make_game_layer(LAYER *layer); + + int save(const char *filename); + int load(const char *filename); + + QUAD *get_selected_quad(); + LAYER *get_selected_layer_type(int index, int type); + LAYER *get_selected_layer(int index); + LAYERGROUP *get_selected_group(); + + class LAYER_GAME *game_layer; + LAYERGROUP *game_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 gui_active; + bool proof_borders; + float mouse_delta_x; + float mouse_delta_y; + float mouse_delta_wx; + float mouse_delta_wy; + + bool animate; + int64 animate_start; + float animate_time; + + int props; + + int show_envelope_editor; + + 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 void render_properties(RECT *toolbox); + + 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 render_properties(RECT *toolbox); + + void get_size(float *w, float *h); + + int image; + array quads; +}; + + +class LAYER_GAME : public LAYER_TILES +{ +public: + LAYER_GAME(int w, int h); + ~LAYER_GAME(); + + virtual void render_properties(RECT *toolbox); +}; diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/ed_layer_game.cpp index 0e002559..4524cb80 100644 --- a/src/game/editor/ed_layer_game.cpp +++ b/src/game/editor/ed_layer_game.cpp @@ -1,5 +1,5 @@ #include -#include "editor.hpp" +#include "ed_editor.hpp" LAYER_GAME::LAYER_GAME(int w, int h) diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/ed_layer_quads.cpp index d58a9e0f..5625876c 100644 --- a/src/game/editor/ed_layer_quads.cpp +++ b/src/game/editor/ed_layer_quads.cpp @@ -1,5 +1,7 @@ -#include "editor.hpp" +#include "ed_editor.hpp" #include +#include +#include LAYER_QUADS::LAYER_QUADS() { @@ -12,88 +14,6 @@ LAYER_QUADS::~LAYER_QUADS() { } -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); -} - -static void render_quads(QUAD *quads, int num_quads) -{ - gfx_quads_begin(); - float conv = 1/255.0f; - for(int i = 0; i < num_quads; i++) - { - QUAD *q = &quads[i]; - - 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 r=1, g=1, b=1, a=1; - float offset_x = 0; - float offset_y = 0; - float rot = 0; - - if(editor.animate) - { - if(q->pos_env >= 0 && q->pos_env < editor.map.envelopes.len()) - { - ENVELOPE *e = editor.map.envelopes[q->pos_env]; - float t = editor.animate_time+q->pos_env_offset/1000.0f; - offset_x = e->eval(t, 0); - offset_y = e->eval(t, 1); - rot = e->eval(t, 2); - } - - if(q->color_env >= 0 && q->color_env < editor.map.envelopes.len()) - { - ENVELOPE *e = editor.map.envelopes[q->color_env]; - float t = editor.animate_time+q->color_env_offset/1000.0f; - r = e->eval(t, 0); - g = e->eval(t, 1); - b = e->eval(t, 2); - a = e->eval(t, 3); - } - } - - 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 LAYER_QUADS::render() { gfx_texture_set(-1); diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/ed_layer_tiles.cpp index f697e56e..583426e3 100644 --- a/src/game/editor/ed_layer_tiles.cpp +++ b/src/game/editor/ed_layer_tiles.cpp @@ -1,84 +1,8 @@ #include #include -#include "editor.hpp" - -static void render_tilemap(TILE *tiles, int w, int h, float scale) -{ - //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); - - // 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(); - - 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(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; - 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); - } - } - - gfx_quads_end(); -} +#include +#include +#include "ed_editor.hpp" LAYER_TILES::LAYER_TILES(int w, int h) { diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp deleted file mode 100644 index 5a9ebf45..00000000 --- a/src/game/editor/editor.cpp +++ /dev/null @@ -1,2501 +0,0 @@ -#include -#include -#include - -extern "C" { - #include - #include - #include - #include -} - -#include -#include -#include -//#include "game/mapres_col.h" -#include -#include - -#include "editor.hpp" - -static int checker_texture = 0; -static int background_texture = 0; -static int cursor_texture = 0; -static int entities_texture = 0; - - -EDITOR editor; - -LAYERGROUP::LAYERGROUP() -{ - name = ""; - visible = true; - game_group = false; - offset_x = 0; - offset_y = 0; - parallax_x = 100; - parallax_y = 100; -} - -LAYERGROUP::~LAYERGROUP() -{ - clear(); -} - -void LAYERGROUP::convert(RECT *rect) -{ - rect->x += offset_x; - rect->y += offset_y; -} - -void LAYERGROUP::mapping(float *points) -{ - mapscreen_to_world( - editor.world_offset_x, editor.world_offset_y, - parallax_x/100.0f, parallax_y/100.0f, - offset_x, offset_y, - gfx_screenaspect(), editor.world_zoom, points); - - points[0] += editor.editor_offset_x; - points[1] += editor.editor_offset_y; - points[2] += editor.editor_offset_x; - points[3] += editor.editor_offset_y; -} - -void LAYERGROUP::mapscreen() -{ - float points[4]; - mapping(points); - gfx_mapscreen(points[0], points[1], points[2], points[3]); -} - -void LAYERGROUP::render() -{ - mapscreen(); - - for(int i = 0; i < layers.len(); i++) - { - if(layers[i]->visible && layers[i] != editor.game_layer) - layers[i]->render(); - } -} - -bool LAYERGROUP::is_empty() const { return layers.len() == 0; } -void LAYERGROUP::clear() { layers.deleteall(); } -void LAYERGROUP::add_layer(LAYER *l) { layers.add(l); } - -void LAYERGROUP::delete_layer(int index) -{ - if(index < 0 || index >= layers.len()) return; - delete layers[index]; - layers.removebyindex(index); -} - -void LAYERGROUP::get_size(float *w, float *h) -{ - *w = 0; *h = 0; - for(int i = 0; i < layers.len(); i++) - { - float lw, lh; - layers[i]->get_size(&lw, &lh); - *w = max(*w, lw); - *h = max(*h, lh); - } -} - - -int LAYERGROUP::swap_layers(int index0, int index1) -{ - if(index0 < 0 || index0 >= layers.len()) return index0; - if(index1 < 0 || index1 >= layers.len()) return index0; - if(index0 == index1) return index0; - swap(layers[index0], layers[index1]); - return index1; -} - -/******************************************************** - OTHER -*********************************************************/ -extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners); -extern void draw_round_rect(float x, float y, float w, float h, float r); - -static 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(); -} - -// copied from gc_menu.cpp, should be more generalized -extern int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hidden=false); - -static vec4 get_button_color(const void *id, int checked) -{ - if(checked < 0) - return vec4(0,0,0,0.5f); - - if(checked > 0) - { - if(ui_hot_item() == id) - return vec4(1,0,0,0.75f); - return vec4(1,0,0,0.5f); - } - - if(ui_hot_item() == id) - return vec4(1,1,1,0.75f); - return vec4(1,1,1,0.5f); -} - -static void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_ALL, 5.0f); - ui_do_label(r, text, 10, 0, -1); -} - -static void draw_editor_button_l(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 5.0f); - ui_do_label(r, text, 10, 0, -1); -} - -static void draw_editor_button_m(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), 0, 5.0f); - ui_do_label(r, text, 10, 0, -1); -} - -static void draw_editor_button_r(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(ui_hot_item() == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 5.0f); - ui_do_label(r, text, 10, 0, -1); -} - -static void draw_inc_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_R, 5.0f); - ui_do_label(r, ">", 10, 0, -1); -} - -static void draw_dec_button(const void *id, const char *text, int checked, const RECT *r, const void *extra) -{ - if(ui_hot_item == id) if(extra) editor.tooltip = (const char *)extra; - ui_draw_rect(r, get_button_color(id, checked), CORNER_L, 5.0f); - ui_do_label(r, "<", 10, 0, -1); -} - - - -static void render_background(RECT view, int texture, float size, float brightness) -{ - gfx_texture_set(texture); - gfx_blend_normal(); - gfx_quads_begin(); - gfx_setcolor(brightness,brightness,brightness,1.0f); - gfx_quads_setsubset(0,0, view.w/size, view.h/size); - gfx_quads_drawTL(view.x, view.y, view.w, view.h); - gfx_quads_end(); -} - - -static int selected_layer = 0; -static int selected_group = 0; -static int selected_quad = -1; -int selected_points = 0; -static int selected_envelope = 0; - -static LAYERGROUP brush; -static LAYER_TILES tileset_picker(16, 16); - -static int ui_do_value_selector(void *id, RECT *r, const char *label, int current, int min, int max, float scale) -{ - /* logic */ - static float value; - int ret = 0; - int inside = ui_mouse_inside(r); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - if(inside) - ret = 1; - editor.lock_mouse = false; - ui_set_active_item(0); - } - else - { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - value += editor.mouse_delta_x*0.05f; - else - value += editor.mouse_delta_x; - - if(fabs(value) > scale) - { - int count = (int)(value/scale); - value = fmod(value, scale); - current += count; - if(current < min) - current = min; - if(current > max) - current = max; - } - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - { - editor.lock_mouse = true; - value = 0; - ui_set_active_item(id); - } - } - - if(inside) - ui_set_hot_item(id); - - // render - char buf[128]; - sprintf(buf, "%s %d", label, current); - ui_draw_rect(r, get_button_color(id, 0), CORNER_ALL, 5.0f); - ui_do_label(r, buf, 12, 0, -1); - return current; -} - - -LAYERGROUP *EDITOR::get_selected_group() -{ - if(selected_group >= 0 && selected_group < editor.map.groups.len()) - return editor.map.groups[selected_group]; - return 0x0; -} - -LAYER *EDITOR::get_selected_layer(int index) -{ - LAYERGROUP *group = get_selected_group(); - if(!group) - return 0x0; - - if(selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len()) - return group->layers[selected_layer]; - return 0x0; -} - -LAYER *EDITOR::get_selected_layer_type(int index, int type) -{ - LAYER *p = get_selected_layer(index); - if(p && p->type == type) - return p; - return 0x0; -} - -QUAD *EDITOR::get_selected_quad() -{ - LAYER_QUADS *ql = (LAYER_QUADS *)get_selected_layer_type(0, LAYERTYPE_QUADS); - if(!ql) - return 0; - if(selected_quad >= 0 && selected_quad < ql->quads.len()) - return &ql->quads[selected_quad]; - return 0; -} - - -static void do_toolbar(RECT toolbar) -{ - RECT button; - - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); - static int zoom_out_button = 0; - if(ui_do_button(&zoom_out_button, "ZO", 0, &button, draw_editor_button_l, "[NumPad-] Zoom out") || inp_key_down(KEY_KP_SUBTRACT)) - editor.zoom_level += 50; - - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); - static int zoom_normal_button = 0; - if(ui_do_button(&zoom_normal_button, "1:1", 0, &button, draw_editor_button_m, "[NumPad*] Zoom to normal and remove editor offset") || inp_key_down(KEY_KP_MULTIPLY)) - { - editor.editor_offset_x = 0; - editor.editor_offset_y = 0; - editor.zoom_level = 100; - } - - ui_vsplit_l(&toolbar, 16.0f, &button, &toolbar); - static int zoom_in_button = 0; - if(ui_do_button(&zoom_in_button, "ZI", 0, &button, draw_editor_button_r, "[NumPad+] Zoom in") || inp_key_down(KEY_KP_ADD)) - editor.zoom_level -= 50; - - if(editor.zoom_level < 50) - editor.zoom_level = 50; - editor.world_zoom = editor.zoom_level/100.0f; - - ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); - - - // brush manipulation - { - int enabled = brush.is_empty()?-1:0; - - // flip buttons - ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); - static int flipx_button = 0; - if(ui_do_button(&flipx_button, "^X", enabled, &button, draw_editor_button_l, "Flip brush horizontal")) - { - for(int i = 0; i < brush.layers.len(); i++) - brush.layers[i]->brush_flip_x(); - } - - ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); - static int flipy_button = 0; - if(ui_do_button(&flipy_button, "^Y", enabled, &button, draw_editor_button_r, "Flip brush vertical")) - { - for(int i = 0; i < brush.layers.len(); i++) - brush.layers[i]->brush_flip_y(); - } - } - - // quad manipulation - { - // do add button - ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); - static int new_button = 0; - - LAYER_QUADS *qlayer = (LAYER_QUADS *)editor.get_selected_layer_type(0, LAYERTYPE_QUADS); - LAYER_TILES *tlayer = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES); - if(ui_do_button(&new_button, "Add Quad", qlayer?0:-1, &button, draw_editor_button, "Adds a new quad")) - { - if(qlayer) - { - float mapping[4]; - LAYERGROUP *g = editor.get_selected_group(); - g->mapping(mapping); - int add_x = f2fx(mapping[0] + (mapping[2]-mapping[0])/2); - int add_y = f2fx(mapping[1] + (mapping[3]-mapping[1])/2); - - QUAD *q = qlayer->new_quad(); - for(int i = 0; i < 5; i++) - { - q->points[i].x += add_x; - q->points[i].y += add_y; - } - } - } - - ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); - static int sq_button = 0; - if(ui_do_button(&sq_button, "Sq. Quad", editor.get_selected_quad()?0:-1, &button, draw_editor_button, "Squares the current quad")) - { - QUAD *q = editor.get_selected_quad(); - if(q) - { - int top = q->points[0].y; - int left = q->points[0].x; - int bottom = q->points[0].y; - int right = q->points[0].x; - - for(int k = 1; k < 4; k++) - { - if(q->points[k].y < top) top = q->points[k].y; - if(q->points[k].x < left) left = q->points[k].x; - if(q->points[k].y > bottom) bottom = q->points[k].y; - if(q->points[k].x > right) right = q->points[k].x; - } - - q->points[0].x = left; q->points[0].y = top; - q->points[1].x = right; q->points[1].y = top; - q->points[2].x = left; q->points[2].y = bottom; - q->points[3].x = right; q->points[3].y = bottom; - } - } - - ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar); - bool in_gamegroup = editor.game_group->layers.find(tlayer) != -1; - static int col_button = 0; - if(ui_do_button(&col_button, "Make Col", (in_gamegroup&&tlayer)?0:-1, &button, draw_editor_button, "Constructs collision from the current layer")) - { - LAYER_TILES *gl = editor.game_layer; - int w = min(gl->width, tlayer->width); - int h = min(gl->height, tlayer->height); - dbg_msg("", "w=%d h=%d", w, h); - 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 = tlayer->tiles[y*tlayer->width+x].index?TILE_SOLID:TILE_AIR; - } - } - } -} - -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); -} - -static void do_quad(QUAD *q, int index) -{ - enum - { - OP_NONE=0, - OP_MOVE_ALL, - OP_MOVE_PIVOT, - OP_ROTATE, - }; - - // some basic values - void *id = &q->points[4]; // use pivot addr as id - static POINT rotate_points[4]; - static float last_wx; - static float last_wy; - static int operation = OP_NONE; - static float rotate_angle = 0; - float wx = ui_mouse_world_x(); - float wy = ui_mouse_world_y(); - - // get pivot - float center_x = fx2f(q->points[4].x); - float center_y = fx2f(q->points[4].y); - - float dx = (center_x - wx); - float dy = (center_y - wy); - if(dx*dx+dy*dy < 10*10) - ui_set_hot_item(id); - - // draw selection background - if(selected_quad == index) - { - gfx_setcolor(0,0,0,1); - gfx_quads_draw(center_x, center_y, 7.0f, 7.0f); - } - - if(ui_active_item() == id) - { - // check if we only should move pivot - if(operation == OP_MOVE_PIVOT) - { - q->points[4].x += f2fx(wx-last_wx); - q->points[4].y += f2fx(wy-last_wy); - } - else if(operation == OP_MOVE_ALL) - { - // move all points including pivot - for(int v = 0; v < 5; v++) - { - q->points[v].x += f2fx(wx-last_wx); - q->points[v].y += f2fx(wy-last_wy); - } - } - else if(operation == OP_ROTATE) - { - for(int v = 0; v < 4; v++) - { - q->points[v] = rotate_points[v]; - rotate(&q->points[4], &q->points[v], rotate_angle); - } - } - - rotate_angle += (editor.mouse_delta_x) * 0.002f; - last_wx = wx; - last_wy = wy; - - if(!ui_mouse_button(0)) - { - editor.lock_mouse = false; - operation = OP_NONE; - ui_set_active_item(0); - } - - gfx_setcolor(1,1,1,1); - } - else if(ui_hot_item() == id) - { - gfx_setcolor(1,1,1,1); - editor.tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate"; - - if(ui_mouse_button(0)) - { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - operation = OP_MOVE_PIVOT; - else if(inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)) - { - editor.lock_mouse = true; - operation = OP_ROTATE; - rotate_angle = 0; - rotate_points[0] = q->points[0]; - rotate_points[1] = q->points[1]; - rotate_points[2] = q->points[2]; - rotate_points[3] = q->points[3]; - } - else - operation = OP_MOVE_ALL; - - ui_set_active_item(id); - selected_quad = index; - editor.props = PROPS_QUAD; - last_wx = wx; - last_wy = wy; - } - } - else - gfx_setcolor(0,1,0,1); - - gfx_quads_draw(center_x, center_y, 5.0f, 5.0f); -} - -static void do_quad_point(QUAD *q, int quad_index, int v) -{ - void *id = &q->points[v]; - - float wx = ui_mouse_world_x(); - float wy = ui_mouse_world_y(); - - float px = fx2f(q->points[v].x); - float py = fx2f(q->points[v].y); - - float dx = (px - wx); - float dy = (py - wy); - if(dx*dx+dy*dy < 10*10) - ui_set_hot_item(id); - - // draw selection background - if(selected_quad == quad_index && selected_points&(1< 0.5f) - moved = true; - } - - if(moved) - { - if(operation == OP_MOVEPOINT) - { - for(int m = 0; m < 4; m++) - if(selected_points&(1<points[m].x += f2fx(dx); - q->points[m].y += f2fx(dy); - } - } - else if(operation == OP_MOVEUV) - { - for(int m = 0; m < 4; m++) - if(selected_points&(1<texcoords[m].x += f2fx(dx*0.001f); - q->texcoords[m].y += f2fx(dy*0.001f); - } - } - } - - if(!ui_mouse_button(0)) - { - if(!moved) - { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - selected_points ^= 1<visible) - editor.map.groups[g]->render(); - } - - // render the game above everything else - if(editor.game_group->visible && editor.game_layer->visible) - { - editor.game_group->mapscreen(); - editor.game_layer->render(); - } - } - - static void *editor_id = (void *)&editor_id; - int inside = ui_mouse_inside(&view); - - // fetch mouse position - float wx = ui_mouse_world_x(); - float wy = ui_mouse_world_y(); - float mx = ui_mouse_x(); - float my = ui_mouse_y(); - - static float start_wx = 0; - static float start_wy = 0; - static float start_mx = 0; - static float start_my = 0; - - enum - { - OP_NONE=0, - OP_BRUSH_GRAB, - OP_BRUSH_DRAW, - OP_PAN_WORLD, - OP_PAN_EDITOR, - }; - - // remap the screen so it can display the whole tileset - if(show_picker) - { - RECT screen = *ui_screen(); - float size = 32.0*16.0f; - float w = size*(screen.w/view.w); - float h = size*(screen.h/view.h); - float x = -(view.x/screen.w)*w; - float y = -(view.y/screen.h)*h; - wx = x+w*mx/screen.w; - wy = y+h*my/screen.h; - gfx_mapscreen(x, y, x+w, y+h); - LAYER_TILES *t = (LAYER_TILES *)editor.get_selected_layer_type(0, LAYERTYPE_TILES); - if(t) - { - tileset_picker.image = t->image; - tileset_picker.tex_id = t->tex_id; - tileset_picker.render(); - } - } - - static int operation = OP_NONE; - - // draw layer borders - LAYER *edit_layers[16]; - int num_edit_layers = 0; - num_edit_layers = 0; - - if(show_picker) - { - edit_layers[0] = &tileset_picker; - num_edit_layers++; - } - else - { - edit_layers[0] = editor.get_selected_layer(0); - if(edit_layers[0]) - num_edit_layers++; - - LAYERGROUP *g = editor.get_selected_group(); - if(g) - { - g->mapscreen(); - - for(int i = 0; i < num_edit_layers; i++) - { - if(edit_layers[i]->type != LAYERTYPE_TILES) - continue; - - float w, h; - edit_layers[i]->get_size(&w, &h); - - gfx_texture_set(-1); - gfx_lines_begin(); - gfx_lines_draw(0,0, w,0); - gfx_lines_draw(w,0, w,h); - gfx_lines_draw(w,h, 0,h); - gfx_lines_draw(0,h, 0,0); - gfx_lines_end(); - } - } - } - - if(inside) - { - ui_set_hot_item(editor_id); - - // do global operations like pan and zoom - if(ui_active_item() == 0 && ui_mouse_button(0)) - { - start_wx = wx; - start_wy = wy; - start_mx = mx; - start_my = my; - - if(inp_key_pressed(KEY_LALT)) - { - if(inp_key_pressed(KEY_LSHIFT)) - operation = OP_PAN_EDITOR; - else - operation = OP_PAN_WORLD; - ui_set_active_item(editor_id); - } - } - - // brush editing - { - if(brush.is_empty()) - editor.tooltip = "Use left mouse button to drag and create a brush."; - else - editor.tooltip = "Use left mouse button to paint with the brush. Right button clears the brush."; - - if(ui_active_item() == editor_id) - { - RECT r; - r.x = start_wx; - r.y = start_wy; - r.w = wx-start_wx; - r.h = wy-start_wy; - if(r.w < 0) - { - r.x += r.w; - r.w = -r.w; - } - - if(r.h < 0) - { - r.y += r.h; - r.h = -r.h; - } - - if(operation == OP_BRUSH_DRAW) - { - if(!brush.is_empty()) - { - // draw with brush - for(int k = 0; k < num_edit_layers; k++) - { - if(edit_layers[k]->type == brush.layers[0]->type) - edit_layers[k]->brush_draw(brush.layers[0], wx, wy); - } - } - } - else if(operation == OP_BRUSH_GRAB) - { - if(!ui_mouse_button(0)) - { - // grab brush - dbg_msg("editor", "grabbing %f %f %f %f", r.x, r.y, r.w, r.h); - - // TODO: do all layers - int grabs = 0; - for(int k = 0; k < num_edit_layers; k++) - grabs += edit_layers[k]->brush_grab(&brush, r); - if(grabs == 0) - brush.clear(); - } - else - { - editor.map.groups[selected_group]->mapscreen(); - for(int k = 0; k < num_edit_layers; k++) - edit_layers[k]->brush_selecting(r); - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - } - } - } - else - { - if(ui_mouse_button(1)) - brush.clear(); - - if(ui_mouse_button(0) && operation == OP_NONE) - { - ui_set_active_item(editor_id); - - if(brush.is_empty()) - operation = OP_BRUSH_GRAB; - else - { - operation = OP_BRUSH_DRAW; - for(int k = 0; k < num_edit_layers; k++) - { - if(edit_layers[k]->type == brush.layers[0]->type) - edit_layers[k]->brush_place(brush.layers[0], wx, wy); - } - - } - } - - if(!brush.is_empty()) - { - brush.offset_x = -(int)wx; - brush.offset_y = -(int)wy; - for(int i = 0; i < brush.layers.len(); i++) - { - if(brush.layers[i]->type == LAYERTYPE_TILES) - { - brush.offset_x = -(int)(wx/32.0f)*32; - brush.offset_y = -(int)(wy/32.0f)*32; - break; - } - } - - LAYERGROUP *g = editor.get_selected_group(); - brush.offset_x += g->offset_x; - brush.offset_y += g->offset_y; - brush.parallax_x = g->parallax_x; - brush.parallax_y = g->parallax_y; - brush.render(); - float w, h; - brush.get_size(&w, &h); - - gfx_texture_set(-1); - gfx_lines_begin(); - gfx_lines_draw(0,0, w,0); - gfx_lines_draw(w,0, w,h); - gfx_lines_draw(w,h, 0,h); - gfx_lines_draw(0,h, 0,0); - gfx_lines_end(); - - } - } - } - - if(!show_picker && brush.is_empty()) - { - // fetch layers - LAYERGROUP *g = editor.get_selected_group(); - if(g) - g->mapscreen(); - - for(int k = 0; k < num_edit_layers; k++) - { - if(edit_layers[k]->type == LAYERTYPE_QUADS) - { - LAYER_QUADS *layer = (LAYER_QUADS *)edit_layers[k]; - - gfx_texture_set(-1); - gfx_quads_begin(); - for(int i = 0; i < layer->quads.len(); i++) - { - for(int v = 0; v < 4; v++) - do_quad_point(&layer->quads[i], i, v); - - do_quad(&layer->quads[i], i); - } - gfx_quads_end(); - } - } - - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - } - - // do panning - if(ui_active_item() == editor_id) - { - if(operation == OP_PAN_WORLD) - { - editor.world_offset_x -= editor.mouse_delta_x*editor.world_zoom; - editor.world_offset_y -= editor.mouse_delta_y*editor.world_zoom; - } - else if(operation == OP_PAN_EDITOR) - { - editor.editor_offset_x -= editor.mouse_delta_x*editor.world_zoom; - editor.editor_offset_y -= editor.mouse_delta_y*editor.world_zoom; - } - } - - - // release mouse - if(!ui_mouse_button(0)) - { - operation = OP_NONE; - ui_set_active_item(0); - } - - } - - // render screen sizes - if(editor.proof_borders) - { - LAYERGROUP *g = editor.game_group; - g->mapscreen(); - - gfx_texture_set(-1); - gfx_lines_begin(); - - float last_points[4]; - float start = 1.0f; //9.0f/16.0f; - float end = 16.0f/9.0f; - const int num_steps = 20; - for(int i = 0; i <= num_steps; i++) - { - float points[4]; - float aspect = start + (end-start)*(i/(float)num_steps); - - mapscreen_to_world( - editor.world_offset_x, editor.world_offset_y, - 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); - - if(i == 0) - { - gfx_lines_draw(points[0], points[1], points[2], points[1]); - gfx_lines_draw(points[0], points[3], points[2], points[3]); - } - - if(i != 0) - { - gfx_lines_draw(points[0], points[1], last_points[0], last_points[1]); - gfx_lines_draw(points[2], points[1], last_points[2], last_points[1]); - gfx_lines_draw(points[0], points[3], last_points[0], last_points[3]); - gfx_lines_draw(points[2], points[3], last_points[2], last_points[3]); - } - - if(i == num_steps) - { - gfx_lines_draw(points[0], points[1], points[0], points[3]); - gfx_lines_draw(points[2], points[1], points[2], points[3]); - } - - mem_copy(last_points, points, sizeof(points)); - } - - if(0) - { - gfx_setcolor(1.0f,0,0,1); - for(int i = 0; i < 4; i++) - { - float points[4]; - float aspects[] = {4.0f/3.0f, 5.0f/4.0f, 16.0f/10.0f, 16.0f/9.0f}; - float aspect = aspects[i]; - - mapscreen_to_world( - editor.world_offset_x, editor.world_offset_y, - 1.0f, 1.0f, 0.0f, 0.0f, aspect, 1.0f, points); - - RECT r; - r.x = points[0]; - r.y = points[1]; - r.w = points[2]-points[0]; - r.h = points[3]-points[1]; - - gfx_lines_draw(r.x, r.y, r.x+r.w, r.y); - gfx_lines_draw(r.x+r.w, r.y, r.x+r.w, r.y+r.h); - gfx_lines_draw(r.x+r.w, r.y+r.h, r.x, r.y+r.h); - gfx_lines_draw(r.x, r.y+r.h, r.x, r.y); - } - } - - gfx_lines_end(); - } - - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - ui_clip_disable(); -} - -int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val) -{ - int change = -1; - - for(int i = 0; props[i].name; i++) - { - RECT slot; - ui_hsplit_t(toolbox, 13.0f, &slot, toolbox); - RECT label, shifter; - ui_vsplit_mid(&slot, &label, &shifter); - ui_hmargin(&shifter, 1.0f, &shifter); - ui_do_label(&label, props[i].name, 10.0f, -1, -1); - - if(props[i].type == PROPTYPE_INT_STEP) - { - RECT inc, dec; - char buf[64]; - - ui_vsplit_r(&shifter, 10.0f, &shifter, &inc); - ui_vsplit_l(&shifter, 10.0f, &dec, &shifter); - sprintf(buf, "%d", props[i].value); - ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); - ui_do_label(&shifter, buf, 10.0f, 0, -1); - - if(ui_do_button(&ids[i], 0, 0, &dec, draw_dec_button, "Decrease")) - { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - *new_val = props[i].value-5; - else - *new_val = props[i].value-1; - change = i; - } - if(ui_do_button(((char *)&ids[i])+1, 0, 0, &inc, draw_inc_button, "Increase")) - { - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - *new_val = props[i].value+5; - else - *new_val = props[i].value+1; - change = i; - } - } - else if(props[i].type == PROPTYPE_INT_SCROLL) - { - int new_value = ui_do_value_selector(&ids[i], &shifter, "", props[i].value, props[i].min, props[i].max, 1.0f); - if(new_value != props[i].value) - { - *new_val = new_value; - change = i; - } - } - else if(props[i].type == PROPTYPE_COLOR) - { - static const char *texts[4] = {"R", "G", "B", "A"}; - static int shift[] = {24, 16, 8, 0}; - int new_color = 0; - - for(int c = 0; c < 4; c++) - { - int v = (props[i].value >> shift[c])&0xff; - new_color |= ui_do_value_selector(((char *)&ids[i])+c, &shifter, texts[c], v, 0, 255, 1.0f)<= 0 && selected_group < editor.map.groups.len()) - valid_group = 1; - - if(valid_group && selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len()) - valid_layer = 1; - - int valid_group_mask = valid_group ? 0 : -1; - int valid_layer_mask = valid_layer ? 0 : -1; - - { - ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); - - // new layer group - ui_vsplit_l(&slot, 12.0f, &button, &slot); - static int new_layer_group_button = 0; - if(ui_do_button(&new_layer_group_button, "G+", 0, &button, draw_editor_button, "New group")) - { - editor.map.new_group(); - selected_group = editor.map.groups.len()-1; - editor.props = PROPS_GROUP; - } - - // new tile layer - ui_vsplit_l(&slot, 10.0f, &button, &slot); - ui_vsplit_l(&slot, 12.0f, &button, &slot); - static int new_tile_layer_button = 0; - if(ui_do_button(&new_tile_layer_button, "T+", valid_group_mask, &button, draw_editor_button, "New tile layer")) - { - LAYER *l = new LAYER_TILES(50, 50); - editor.map.groups[selected_group]->add_layer(l); - selected_layer = editor.map.groups[selected_group]->layers.len()-1; - editor.props = PROPS_LAYER; - } - - // new quad layer - ui_vsplit_l(&slot, 2.0f, &button, &slot); - ui_vsplit_l(&slot, 12.0f, &button, &slot); - static int new_quad_layer_button = 0; - if(ui_do_button(&new_quad_layer_button, "Q+", valid_group_mask, &button, draw_editor_button, "New quad layer")) - { - LAYER *l = new LAYER_QUADS; - editor.map.groups[selected_group]->add_layer(l); - selected_layer = editor.map.groups[selected_group]->layers.len()-1; - editor.props = PROPS_LAYER; - } - - // remove layer - ui_vsplit_r(&slot, 12.0f, &slot, &button); - static int delete_layer_button = 0; - if(ui_do_button(&delete_layer_button, "L-", valid_layer_mask, &button, draw_editor_button, "Delete layer")) - editor.map.groups[selected_group]->delete_layer(selected_layer); - - // remove group - ui_vsplit_r(&slot, 2.0f, &slot, &button); - ui_vsplit_r(&slot, 12.0f, &slot, &button); - static int delete_group_button = 0; - if(ui_do_button(&delete_group_button, "G-", valid_group_mask, &button, draw_editor_button, "Delete group")) - editor.map.delete_group(selected_group); - } - - ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox); - - // render layers - { - for(int g = 0; g < editor.map.groups.len(); g++) - { - RECT visible_toggle; - ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); - ui_vsplit_l(&slot, 12, &visible_toggle, &slot); - if(ui_do_button(&editor.map.groups[g]->visible, editor.map.groups[g]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, "Toggle group visibility")) - editor.map.groups[g]->visible = !editor.map.groups[g]->visible; - - sprintf(buf, "#%d %s", g, editor.map.groups[g]->name); - if(ui_do_button(&editor.map.groups[g], buf, g==selected_group, &slot, draw_editor_button_r, "Select group")) - { - selected_group = g; - selected_layer = 0; - editor.props = PROPS_GROUP; - } - - ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox); - - for(int i = 0; i < editor.map.groups[g]->layers.len(); i++) - { - //visible - ui_hsplit_t(&layersbox, 12.0f, &slot, &layersbox); - ui_vsplit_l(&slot, 12.0f, 0, &button); - ui_vsplit_l(&button, 15, &visible_toggle, &button); - - if(ui_do_button(&editor.map.groups[g]->layers[i]->visible, editor.map.groups[g]->layers[i]->visible?"V":"H", 0, &visible_toggle, draw_editor_button_l, "Toggle layer visibility")) - editor.map.groups[g]->layers[i]->visible = !editor.map.groups[g]->layers[i]->visible; - - sprintf(buf, "#%d %s ", i, editor.map.groups[g]->layers[i]->type_name); - if(ui_do_button(editor.map.groups[g]->layers[i], buf, g==selected_group&&i==selected_layer, &button, draw_editor_button_r, "Select layer")) - { - selected_layer = i; - selected_group = g; - editor.props = PROPS_LAYER; - } - ui_hsplit_t(&layersbox, 2.0f, &slot, &layersbox); - } - ui_hsplit_t(&layersbox, 5.0f, &slot, &layersbox); - } - } - - propsbox = layersbox; - - // group properties - if(editor.props == PROPS_GROUP && valid_group) - { - ui_hsplit_t(&propsbox, 12.0f, &slot, &propsbox); - ui_do_label(&slot, "Group Props", 12.0f, -1, -1); - - enum - { - PROP_ORDER=0, - PROP_POS_X, - PROP_POS_Y, - PROP_PARA_X, - PROP_PARA_Y, - NUM_PROPS, - }; - - PROPERTY props[] = { - {"Order", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1}, - {"Pos X", -editor.map.groups[selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Pos Y", -editor.map.groups[selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Para X", editor.map.groups[selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000}, - {"Para Y", editor.map.groups[selected_group]->parallax_y, 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(&propsbox, props, ids, &new_val); - if(prop == PROP_ORDER) - selected_group = editor.map.swap_groups(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[selected_group]->parallax_x = new_val; - else if(prop == PROP_PARA_Y) - editor.map.groups[selected_group]->parallax_y = new_val; - else if(prop == PROP_POS_X) - editor.map.groups[selected_group]->offset_x = -new_val; - else if(prop == PROP_POS_Y) - editor.map.groups[selected_group]->offset_y = -new_val; - } - } - - // layer properties - if(editor.get_selected_layer(0)) - { - LAYERGROUP *current_group = editor.map.groups[selected_group]; - LAYER *current_layer = editor.get_selected_layer(0); - - if(editor.props == PROPS_LAYER) - { - ui_hsplit_t(&propsbox, 15.0f, &slot, &propsbox); - ui_do_label(&slot, "Layer Props", 12.0f, -1, -1); - - enum - { - PROP_GROUP=0, - PROP_ORDER, - NUM_PROPS, - }; - - PROPERTY props[] = { - {"Group", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1}, - {"Order", selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()}, - {0}, - }; - - static int ids[NUM_PROPS] = {0}; - int new_val = 0; - int prop = editor.do_properties(&propsbox, props, ids, &new_val); - - if(prop == PROP_ORDER) - selected_layer = current_group->swap_layers(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); - selected_group = new_val; - selected_layer = editor.map.groups[new_val]->layers.len()-1; - } - } - } - - current_layer->render_properties(&propsbox); - } -} - -static void render_images(RECT toolbox, RECT view) -{ - static int selected_image = 0; - - for(int i = 0; i < editor.map.images.len(); i++) - { - char buf[128]; - sprintf(buf, "#%d %dx%d", i, editor.map.images[i]->width, editor.map.images[i]->height); - RECT slot; - ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); - - if(ui_do_button(&editor.map.images[i], buf, selected_image == i, &slot, draw_editor_button, "Select image")) - selected_image = i; - - ui_hsplit_t(&toolbox, 2.0f, 0, &toolbox); - - // render image - if(selected_image == i) - { - RECT r; - ui_margin(&view, 10.0f, &r); - if(r.h < r.w) - r.w = r.h; - else - r.h = r.w; - gfx_texture_set(editor.map.images[i]->tex_id); - gfx_blend_normal(); - gfx_quads_begin(); - gfx_quads_drawTL(r.x, r.y, r.w, r.h); - gfx_quads_end(); - - } - } - - RECT slot; - ui_hsplit_t(&toolbox, 5.0f, &slot, &toolbox); - ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); - - // new image - static int new_image_button = 0; - if(ui_do_button(&new_image_button, "(Load New Image)", 0, &slot, draw_editor_button, "Load a new image to use in the map")) - editor.dialog = DIALOG_LOAD_IMAGE; - - ui_hsplit_t(&toolbox, 15.0f, &slot, &toolbox); -} - -static void editor_listdir_callback(const char *name, int is_dir, void *user) -{ - if(name[0] == '.') // skip this shit! - return; - - RECT *view = (RECT *)user; - RECT button; - ui_hsplit_t(view, 15.0f, &button, view); - ui_hsplit_t(view, 2.0f, 0, view); - - if(ui_do_button((void*)(10+(int)button.y), name, 0, &button, draw_editor_button, 0)) - { - char buf[512]; - sprintf(buf, "tilesets/%s", name); - - IMAGE imginfo; - if(!gfx_load_png(&imginfo, buf)) - return; - - IMAGE *img = new IMAGE; - *img = imginfo; - img->tex_id = gfx_load_texture_raw(imginfo.width, imginfo.height, imginfo.format, imginfo.data, IMG_AUTO); - editor.map.images.add(img); - - //tilesets_set_img(tilesets_new(), img.width, img.height, img.data); - editor.dialog = DIALOG_NONE; - } -} - -static void render_dialog_load_image() -{ - // GUI coordsys - gfx_clear(0.25f,0.25f,0.25f); - - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - - RECT view = *ui_screen(); - fs_listdir("tilesets", editor_listdir_callback, &view); - - if(inp_key_pressed(KEY_ESC)) - editor.dialog = DIALOG_NONE; -} - -static void render_modebar(RECT view) -{ - RECT button; - - // mode buttons - { - ui_vsplit_l(&view, 40.0f, &button, &view); - static int map_button = 0; - if(ui_do_button(&map_button, "Map", editor.mode == MODE_MAP, &button, draw_editor_button_l, "Switch to edit global map settings")) - editor.mode = MODE_MAP; - - ui_vsplit_l(&view, 40.0f, &button, &view); - static int tile_button = 0; - if(ui_do_button(&tile_button, "Layers", editor.mode == MODE_LAYERS, &button, draw_editor_button_m, "Switch to edit layers")) - editor.mode = MODE_LAYERS; - - ui_vsplit_l(&view, 40.0f, &button, &view); - static int img_button = 0; - if(ui_do_button(&img_button, "Images", editor.mode == MODE_IMAGES, &button, draw_editor_button_r, "Switch to manage images")) - editor.mode = MODE_IMAGES; - } - - ui_vsplit_l(&view, 5.0f, 0, &view); - - // animate button - ui_vsplit_l(&view, 30.0f, &button, &view); - static int animate_button = 0; - if(ui_do_button(&animate_button, "Anim", editor.animate, &button, draw_editor_button, "[ctrl+m] Toggle animation") || - (inp_key_down('M') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) - { - editor.animate_start = time_get(); - editor.animate = !editor.animate; - } - - ui_vsplit_l(&view, 5.0f, 0, &view); - - // proof button - ui_vsplit_l(&view, 30.0f, &button, &view); - static int proof_button = 0; - if(ui_do_button(&proof_button, "Proof", editor.proof_borders, &button, draw_editor_button, "[ctrl-p] Toggles proof borders. These borders represent what a player maximum can see.") || - (inp_key_down('P') && (inp_key_pressed(KEY_LCTRL) || inp_key_pressed(KEY_RCTRL)))) - { - editor.proof_borders = !editor.proof_borders; - } - - // spacing - //ui_vsplit_l(&view, 10.0f, 0, &view); -} - -static void render_statusbar(RECT view) -{ - RECT button; - ui_vsplit_r(&view, 60.0f, &view, &button); - static int envelope_button = 0; - if(ui_do_button(&envelope_button, "Envelopes", editor.show_envelope_editor, &button, draw_editor_button, "Toggles the envelope editor")) - editor.show_envelope_editor = (editor.show_envelope_editor+1)%4; - - if(editor.tooltip) - ui_do_label(&view, editor.tooltip, 12.0f, -1, -1); -} - -static void render_envelopeeditor(RECT view) -{ - if(selected_envelope < 0) selected_envelope = 0; - if(selected_envelope >= editor.map.envelopes.len()) selected_envelope--; - - ENVELOPE *envelope = 0; - if(selected_envelope >= 0 && selected_envelope < editor.map.envelopes.len()) - envelope = editor.map.envelopes[selected_envelope]; - - bool show_colorbar = false; - if(envelope && envelope->channels == 4) - show_colorbar = true; - - RECT toolbar, curvebar, colorbar; - ui_hsplit_t(&view, 20.0f, &toolbar, &view); - ui_hsplit_t(&view, 20.0f, &curvebar, &view); - ui_margin(&toolbar, 2.0f, &toolbar); - ui_margin(&curvebar, 2.0f, &curvebar); - - if(show_colorbar) - { - ui_hsplit_t(&view, 20.0f, &colorbar, &view); - ui_margin(&colorbar, 2.0f, &colorbar); - render_background(colorbar, checker_texture, 16.0f, 1.0f); - } - - render_background(view, checker_texture, 32.0f, 0.1f); - - // do the toolbar - { - RECT button; - ENVELOPE *new_env = 0; - - ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button); - static int new_4d_button = 0; - if(ui_do_button(&new_4d_button, "Color+", 0, &button, draw_editor_button, "Creates a new color envelope")) - new_env = editor.map.new_envelope(4); - - ui_vsplit_r(&toolbar, 5.0f, &toolbar, &button); - ui_vsplit_r(&toolbar, 50.0f, &toolbar, &button); - static int new_2d_button = 0; - if(ui_do_button(&new_2d_button, "Pos.+", 0, &button, draw_editor_button, "Creates a new pos envelope")) - new_env = editor.map.new_envelope(3); - - if(new_env) // add the default points - { - if(new_env->channels == 4) - { - new_env->add_point(0, 1,1,1,1); - new_env->add_point(1000, 1,1,1,1); - } - else - { - new_env->add_point(0, 0); - new_env->add_point(1000, 0); - } - } - - RECT shifter, inc, dec; - ui_vsplit_l(&toolbar, 60.0f, &shifter, &toolbar); - ui_vsplit_r(&shifter, 15.0f, &shifter, &inc); - ui_vsplit_l(&shifter, 15.0f, &dec, &shifter); - char buf[512]; - sprintf(buf, "%d/%d", selected_envelope+1, editor.map.envelopes.len()); - ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f); - ui_do_label(&shifter, buf, 14.0f, 0, -1); - - static int prev_button = 0; - if(ui_do_button(&prev_button, 0, 0, &dec, draw_dec_button, "Previous Envelope")) - selected_envelope--; - - static int next_button = 0; - if(ui_do_button(&next_button, 0, 0, &inc, draw_inc_button, "Next Envelope")) - selected_envelope++; - - if(envelope) - { - ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); - ui_vsplit_l(&toolbar, 35.0f, &button, &toolbar); - ui_do_label(&button, "Name:", 14.0f, -1, -1); - - ui_vsplit_l(&toolbar, 80.0f, &button, &toolbar); - static int name_box = 0; - ui_do_edit_box(&name_box, &button, envelope->name, sizeof(envelope->name)); - } - } - - if(envelope) - { - static array selection; - static int envelope_editor_id = 0; - static int active_channels = 0xf; - - if(envelope) - { - RECT button; - - ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); - - static const char *names[4][4] = { - {"X", "", "", ""}, - {"X", "Y", "", ""}, - {"X", "Y", "R", ""}, - {"R", "G", "B", "A"}, - }; - - static int channel_buttons[4] = {0}; - int bit = 1; - ui_draw_button_func draw_func; - - for(int i = 0; i < envelope->channels; i++, bit<<=1) - { - ui_vsplit_l(&toolbar, 15.0f, &button, &toolbar); - - if(i == 0) draw_func = draw_editor_button_l; - else if(i == envelope->channels-1) draw_func = draw_editor_button_r; - else draw_func = draw_editor_button_m; - - if(ui_do_button(&channel_buttons[i], names[envelope->channels-1][i], active_channels&bit, &button, draw_func, 0)) - active_channels ^= bit; - } - } - - float end_time = envelope->end_time(); - if(end_time < 1) - end_time = 1; - - envelope->find_top_bottom(); - float top = envelope->top; - float bottom = envelope->bottom; - - if(top < 1) - top = 1; - if(bottom >= 0) - bottom = 0; - - float timescale = end_time/view.w; - float valuescale = (top-bottom)/view.h; - - if(ui_mouse_inside(&view)) - ui_set_hot_item(&envelope_editor_id); - - if(ui_hot_item() == &envelope_editor_id) - { - // do stuff - if(envelope) - { - if(ui_mouse_button_clicked(1)) - { - // add point - int time = (int)(((ui_mouse_x()-view.x)*timescale)*1000.0f); - //float env_y = (ui_mouse_y()-view.y)/timescale; - envelope->add_point(time, - f2fx(envelope->eval(time, 0)), - f2fx(envelope->eval(time, 1)), - f2fx(envelope->eval(time, 2)), - f2fx(envelope->eval(time, 3))); - } - - editor.tooltip = "Press right mouse button to create a new point"; - } - } - - vec3 colors[] = {vec3(1,0.2f,0.2f), vec3(0.2f,1,0.2f), vec3(0.2f,0.2f,1), vec3(1,1,0.2f)}; - - // render lines - { - gfx_texture_set(-1); - gfx_lines_begin(); - for(int c = 0; c < envelope->channels; c++) - { - if(active_channels&(1<eval(0.000001f, c); - int steps = (int)((view.w/ui_screen()->w) * gfx_screenwidth()); - for(int i = 1; i <= steps; i++) - { - float a = i/(float)steps; - float v = envelope->eval(a*end_time, c); - v = (v-bottom)/(top-bottom); - - gfx_lines_draw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h); - prev_x = a; - prev_value = v; - } - } - gfx_lines_end(); - } - - // render curve options - { - for(int i = 0; i < envelope->points.len()-1; i++) - { - float t0 = envelope->points[i].time/1000.0f/end_time; - float t1 = envelope->points[i+1].time/1000.0f/end_time; - - //dbg_msg("", "%f", end_time); - - RECT v; - v.x = curvebar.x + (t0+(t1-t0)*0.5f) * curvebar.w; - v.y = curvebar.y; - v.h = curvebar.h; - v.w = curvebar.h; - v.x -= v.w/2; - void *id = &envelope->points[i].curvetype; - const char *type_name[] = { - "N", "L", "S", "F", "M" - }; - - if(ui_do_button(id, type_name[envelope->points[i].curvetype], 0, &v, draw_editor_button, "Switch curve type")) - envelope->points[i].curvetype = (envelope->points[i].curvetype+1)%NUM_CURVETYPES; - } - } - - // render colorbar - if(show_colorbar) - { - gfx_texture_set(-1); - gfx_quads_begin(); - for(int i = 0; i < envelope->points.len()-1; i++) - { - float r0 = fx2f(envelope->points[i].values[0]); - float g0 = fx2f(envelope->points[i].values[1]); - float b0 = fx2f(envelope->points[i].values[2]); - float a0 = fx2f(envelope->points[i].values[3]); - float r1 = fx2f(envelope->points[i+1].values[0]); - float g1 = fx2f(envelope->points[i+1].values[1]); - float b1 = fx2f(envelope->points[i+1].values[2]); - float a1 = fx2f(envelope->points[i+1].values[3]); - - gfx_setcolorvertex(0, r0, g0, b0, a0); - gfx_setcolorvertex(1, r1, g1, b1, a1); - gfx_setcolorvertex(2, r1, g1, b1, a1); - gfx_setcolorvertex(3, r0, g0, b0, a0); - - float x0 = envelope->points[i].time/1000.0f/end_time; -// float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); - float x1 = envelope->points[i+1].time/1000.0f/end_time; - //float y1 = (fx2f(envelope->points[i+1].values[c])-bottom)/(top-bottom); - RECT v; - v.x = colorbar.x + x0*colorbar.w; - v.y = colorbar.y; - v.w = (x1-x0)*colorbar.w; - v.h = colorbar.h; - - gfx_quads_drawTL(v.x, v.y, v.w, v.h); - } - gfx_quads_end(); - } - - // render handles - { - static bool move = false; - - int current_value = 0, current_time = 0; - - gfx_texture_set(-1); - gfx_quads_begin(); - for(int c = 0; c < envelope->channels; c++) - { - if(!(active_channels&(1<points.len(); i++) - { - float x0 = envelope->points[i].time/1000.0f/end_time; - float y0 = (fx2f(envelope->points[i].values[c])-bottom)/(top-bottom); - RECT final; - final.x = view.x + x0*view.w; - final.y = view.y+view.h - y0*view.h; - final.x -= 2.0f; - final.y -= 2.0f; - final.w = 4.0f; - final.h = 4.0f; - - void *id = &envelope->points[i].values[c]; - - if(ui_mouse_inside(&final)) - ui_set_hot_item(id); - - float colormod = 1.0f; - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - ui_set_active_item(0); - move = false; - } - else - { - envelope->points[i].values[c] -= f2fx(editor.mouse_delta_y*valuescale); - if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT)) - { - if(i != 0) - { - envelope->points[i].time += (int)((editor.mouse_delta_x*timescale)*1000.0f); - if(envelope->points[i].time < envelope->points[i-1].time) - envelope->points[i].time = envelope->points[i-1].time + 1; - if(i+1 != envelope->points.len() && envelope->points[i].time > envelope->points[i+1].time) - envelope->points[i].time = envelope->points[i+1].time - 1; - } - } - } - - colormod = 100.0f; - gfx_setcolor(1,1,1,1); - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - { - selection.clear(); - selection.add(i); - ui_set_active_item(id); - } - - // remove point - if(ui_mouse_button_clicked(1)) - envelope->points.removebyindex(i); - - colormod = 100.0f; - gfx_setcolor(1,0.75f,0.75f,1); - editor.tooltip = "Left mouse to drag. Hold shift to alter time point aswell. Right click to delete."; - } - - if(ui_active_item() == id || ui_hot_item() == id) - { - current_time = envelope->points[i].time; - current_value = envelope->points[i].values[c]; - } - - gfx_setcolor(colors[c].r*colormod, colors[c].g*colormod, colors[c].b*colormod, 1.0f); - gfx_quads_drawTL(final.x, final.y, final.w, final.h); - } - } - gfx_quads_end(); - - char buf[512]; - sprintf(buf, "%.3f %.3f", current_time/1000.0f, fx2f(current_value)); - ui_do_label(&toolbar, buf, 14.0f, 0, -1); - } - } -} - -static void editor_render() -{ - // basic start - gfx_clear(1.0f,0.0f,1.0f); - RECT view = *ui_screen(); - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - - // reset tip - editor.tooltip = 0; - - // render checker - render_background(view, checker_texture, 32.0f, 1.0f); - - RECT modebar, toolbar, statusbar, envelope_editor, propsbar; - - if(editor.gui_active) - { - - ui_hsplit_t(&view, 16.0f, &toolbar, &view); - ui_vsplit_l(&view, 80.0f, &propsbar, &view); - ui_hsplit_b(&view, 16.0f, &view, &statusbar); - - - float brightness = 0.25f; - - render_background(propsbar, background_texture, 128.0f, brightness); - ui_margin(&propsbar, 2.0f, &propsbar); - - render_background(toolbar, background_texture, 128.0f, brightness); - ui_margin(&toolbar, 2.0f, &toolbar); - ui_vsplit_l(&toolbar, 220.0f, &modebar, &toolbar); - - render_background(statusbar, background_texture, 128.0f, brightness); - ui_margin(&statusbar, 2.0f, &statusbar); - - if(editor.show_envelope_editor) - { - float size = 125.0f; - if(editor.show_envelope_editor == 2) - size *= 2.0f; - else if(editor.show_envelope_editor == 3) - size *= 3.0f; - ui_hsplit_b(&view, size, &view, &envelope_editor); - render_background(envelope_editor, background_texture, 128.0f, brightness); - ui_margin(&envelope_editor, 2.0f, &envelope_editor); - } - } - - if(editor.dialog == DIALOG_LOAD_IMAGE) - render_dialog_load_image(); - else if(editor.mode == MODE_LAYERS) - render_layers(propsbar, toolbar, view); - else if(editor.mode == MODE_IMAGES) - render_images(propsbar, view); - - gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h); - - if(editor.gui_active) - { - render_modebar(modebar); - if(editor.show_envelope_editor) - render_envelopeeditor(envelope_editor); - render_statusbar(statusbar); - } - - //do_propsdialog(); - - // render butt ugly mouse cursor - float mx = ui_mouse_x(); - float my = ui_mouse_y(); - gfx_texture_set(cursor_texture); - gfx_quads_begin(); - gfx_quads_drawTL(mx,my, 16.0f, 16.0f); - gfx_quads_end(); -} - -void editor_reset(bool create_default=true) -{ - editor.map.groups.deleteall(); - editor.map.envelopes.deleteall(); - editor.map.images.deleteall(); - - editor.game_layer = 0; - editor.game_group = 0; - - // create default layers - if(create_default) - { - editor.make_game_group(editor.map.new_group()); - editor.make_game_layer(new LAYER_GAME(50, 50)); - editor.game_group->add_layer(editor.game_layer); - } -} - -void EDITOR::make_game_layer(LAYER *layer) -{ - editor.game_layer = (LAYER_GAME *)layer; - editor.game_layer->tex_id = entities_texture; - editor.game_layer->readonly = true; -} - -void EDITOR::make_game_group(LAYERGROUP *group) -{ - editor.game_group = group; - editor.game_group->game_group = true; - editor.game_group->name = "Game"; -} - -typedef struct -{ - int version; - int width; - int height; - int external; - int image_name; - int image_data; -} MAPITEM_IMAGE; - -typedef struct -{ - int version; - int offset_x; - int offset_y; - int parallax_x; - int parallax_y; - - int start_layer; - int num_layers; -} MAPITEM_GROUP; - -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; - -template -static int make_version(int i, const T &v) -{ return (i<<16)+sizeof(T); } - -enum -{ - MAPITEMTYPE_VERSION=0, - MAPITEMTYPE_INFO, - MAPITEMTYPE_IMAGE, - MAPITEMTYPE_ENVELOPE, - MAPITEMTYPE_GROUP, - MAPITEMTYPE_LAYER, -}; - - -void editor_load_old(DATAFILE *df) -{ - // 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 < editor.map.groups[0]->layers.len()-1; i++) - { - if(editor.map.groups[0]->layers[i] == editor.game_layer) - editor.map.groups[0]->swap_layers(i, i+1); - } - - game_width = tmap->width; - game_height = tmap->height; - } - - // add new layer - editor.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); - - IMAGE *img = new 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); - editor.map.images.add(img); - - // unload image - datafile_unload_data(df, imgres->image_data); - } - } - - // load entities - { - LAYER_GAME *g = editor.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_ROCKET; - 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_HEATH_1; - } - - if(id > 0 && x >= 0 && x < g->width && y >= 0 && y < g->height) - g->tiles[y*g->width+x].index = id; - } - } - } -} - -int EDITOR::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 < map.images.len(); i++) - { - IMAGE *img = map.images[i]; - MAPITEM_IMAGE item; - item.version = 1; - - item.width = img->width; - item.height = img->height; - item.external = 0; - item.image_name = -1; - 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 < map.groups.len(); g++) - { - LAYERGROUP *group = map.groups[g]; - MAPITEM_GROUP gitem; - gitem.version = 1; - - 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.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]; - MAPITEM_LAYER_TILEMAP item; - item.version = 2; - - item.layer.flags = 0; - 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 = 0; - 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); - } - - // finish the data file - datafile_finish(df); - dbg_msg("editor", "done"); - return 1; -} - - -int EDITOR::load(const char *filename) -{ - DATAFILE *df = datafile_load(filename); - if(!df) - return 0; - - // check version - MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0); - if(!item) - { - // import old map - editor_reset(); - editor_load_old(df); - } - 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); - void *data = datafile_get_data(df, item->image_data); - - IMAGE *img = new IMAGE; - img->width = item->width; - img->height = item->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); - editor.map.images.add(img); - - // unload image - datafile_unload_data(df, item->image_data); - } - } - - // 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); - LAYERGROUP *group = map.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; - - for(int l = 0; l < gitem->num_layers; l++) - { - 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); - editor.make_game_layer(tiles); - make_game_group(group); - } - else - tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height); - - 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 *layer = new LAYER_QUADS; - layer->image = quads_item->image; - if(layer->image < -1 || layer->image >= map.images.len()) - layer->image = -1; - void *data = datafile_get_data_swapped(df, quads_item->data); - group->add_layer(layer); - layer->quads.setsize(quads_item->num_quads); - mem_copy(layer->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads); - datafile_unload_data(df, quads_item->data); - } - } - } - } - } - - datafile_unload(df); - - return 0; -} - - -extern "C" void editor_init() -{ - checker_texture = gfx_load_texture("data/editor/checker.png", IMG_AUTO); - background_texture = gfx_load_texture("data/editor/background.png", IMG_AUTO); - cursor_texture = gfx_load_texture("data/editor/cursor.png", IMG_AUTO); - entities_texture = gfx_load_texture("data/editor/entities.png", IMG_AUTO); - - tileset_picker.make_palette(); - tileset_picker.readonly = true; - - editor_reset(); - //editor.load("debug_test.map"); - -#if 0 - IMAGE *img = new IMAGE; - gfx_load_png(img, "tilesets/grassland_main.png"); - img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data); - editor.map.images.add(img); - - - ENVELOPE *e = editor.map.new_envelope(4); - e->add_point(0, 0, 0); - e->add_point(1000, f2fx(1), f2fx(0.75f)); - e->add_point(2000, f2fx(0.75f), f2fx(1)); - e->add_point(3000, 0, 0); - - editor.animate = true; - editor.animate_start = time_get(); - - editor.show_envelope_editor = 1; -#endif - - if(1) - { - float w, h; - float amount = 1300*1000; - float max = 1500; - dbg_msg("", "%f %f %f %f", (900*(5/4.0f))*900.0f, (900*(4/3.0f))*900.0f, (900*(16/9.0f))*900.0f, (900*(16/10.0f))*900.0f); - dbg_msg("", "%f", 900*(16/9.0f)); - calc_screen_params(amount, max, max, 5.0f/4.0f, &w, &h); dbg_msg("", "5:4 %f %f %f", w, h, w*h); - calc_screen_params(amount, max, max, 4.0f/3.0f, &w, &h); dbg_msg("", "4:3 %f %f %f", w, h, w*h); - calc_screen_params(amount, max, max, 16.0f/9.0f, &w, &h); dbg_msg("", "16:9 %f %f %f", w, h, w*h); - calc_screen_params(amount, max, max, 16.0f/10.0f, &w, &h); dbg_msg("", "16:10 %f %f %f", w, h, w*h); - - calc_screen_params(amount, max, max, 9.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h); - calc_screen_params(amount, max, max, 16.0f/3.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h); - calc_screen_params(amount, max, max, 3.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h); - } -} - -extern "C" void editor_update_and_render() -{ - static int mouse_x = 0; - static int mouse_y = 0; - - editor.animate_time = (time_get()-editor.animate_start)/(float)time_freq(); - - // handle mouse movement - float mx, my, mwx, mwy; - int rx, ry; - { - inp_mouse_relative(&rx, &ry); - editor.mouse_delta_x = rx; - editor.mouse_delta_y = ry; - - if(!editor.lock_mouse) - { - mouse_x += rx; - mouse_y += ry; - } - - if(mouse_x < 0) mouse_x = 0; - if(mouse_y < 0) mouse_y = 0; - if(mouse_x > ui_screen()->w) mouse_x = (int)ui_screen()->w; - if(mouse_y > ui_screen()->h) mouse_y = (int)ui_screen()->h; - - // update the ui - mx = mouse_x; - my = mouse_y; - mwx = 0; - mwy = 0; - - // fix correct world x and y - LAYERGROUP *g = editor.get_selected_group(); - if(g) - { - float points[4]; - g->mapping(points); - - float world_width = points[2]-points[0]; - float world_height = points[3]-points[1]; - - mwx = points[0] + world_width * (mouse_x/ui_screen()->w); - mwy = points[1] + world_height * (mouse_y/ui_screen()->h); - editor.mouse_delta_wx = editor.mouse_delta_x*(world_width / ui_screen()->w); - editor.mouse_delta_wy = editor.mouse_delta_y*(world_height / ui_screen()->h); - } - - int buttons = 0; - if(inp_key_pressed(KEY_MOUSE_1)) buttons |= 1; - if(inp_key_pressed(KEY_MOUSE_2)) buttons |= 2; - if(inp_key_pressed(KEY_MOUSE_3)) buttons |= 4; - - ui_update(mx,my,mwx,mwy,buttons); - } - - // toggle gui - if(inp_key_down(KEY_TAB)) - editor.gui_active = !editor.gui_active; - - if(inp_key_down(KEY_F5)) - editor.save("data/maps/debug_test2.map"); - - if(inp_key_down(KEY_F8)) - editor.load("data/maps/debug_test.map"); - - editor_render(); - inp_clear_events(); -} - diff --git a/src/game/editor/editor.hpp b/src/game/editor/editor.hpp deleted file mode 100644 index 0d2af5e1..00000000 --- a/src/game/editor/editor.hpp +++ /dev/null @@ -1,569 +0,0 @@ -#include -#include -#include "array.h" - -extern "C" { - #include - #include - #include - #include -} - -#include - -// EDITOR SPECIFIC -template -void swap(T &a, T &b) -{ - T tmp = a; - a = b; - b = tmp; -} - -enum -{ - MODE_MAP=0, - MODE_LAYERS, - MODE_IMAGES, - - DIALOG_NONE=0, - DIALOG_LOAD_IMAGE, - - LAYERTYPE_INVALID=0, - LAYERTYPE_GAME, - LAYERTYPE_TILES, - LAYERTYPE_QUADS, -}; - -typedef struct -{ - int x, y; // 22.10 fixed point -} POINT; - -// float to fixed -inline int f2fx(float v) { return (int)(v*(float)(1<<10)); } -inline float fx2f(int v) { return v*(1.0f/(1<<10)); } - -typedef struct // as in file -{ - int r, g, b, a; -} COLOR; - -typedef struct // as in file -{ - 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 // as in file -{ - POINT position; - int type; -} ENTITY; - -enum -{ - ENTITY_NULL=0, - ENTITY_SPAWN, - ENTITY_SPAWN_RED, - ENTITY_SPAWN_BLUE, - ENTITY_FLAGSTAND_RED, - ENTITY_FLAGSTAND_BLUE, - ENTITY_ARMOR_1, - ENTITY_HEATH_1, - ENTITY_WEAPON_SHOTGUN, - ENTITY_WEAPON_ROCKET, - ENTITY_POWERUP_NINJA, - NUM_ENTITIES, - - TILE_AIR=0, - TILE_SOLID, - TILE_NOHOOK, - - ENTITY_OFFSET=255-16*4, -}; - -typedef struct // as in file -{ - unsigned char index; - unsigned char flags; - unsigned char reserved1; - unsigned char reserved2; -} TILE; - -enum -{ - CURVETYPE_STEP=0, - CURVETYPE_LINEAR, - CURVETYPE_SLOW, - CURVETYPE_FAST, - CURVETYPE_SMOOTH, - NUM_CURVETYPES, - -}; - -typedef struct // as in file -{ - int time; // in ms - int curvetype; - int values[4]; // 1-4 depending on envelope (22.10 fixed point) -} ENVPOINT; - -class ENVELOPE -{ -public: - int channels; - array 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(); - } - - void find_top_bottom() - { - top = -1000000000.0f; - bottom = 1000000000.0f; - for(int i = 0; i < points.len(); i++) - { - for(int c = 0; c < channels; c++) - { - float v = fx2f(points[i].values[c]); - if(v > top) top = v; - if(v < bottom) bottom = v; - } - } - } - - float eval(float time, int channel) - { - if(channel >= channels) - return 0; - if(points.len() == 0) - return 0; - if(points.len() == 1) - return points[0].values[channel]; - - time = fmod(time, end_time())*1000.0f; - for(int i = 0; i < points.len() - 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; - - float v0 = fx2f(points[i].values[channel]); - float v1 = fx2f(points[i+1].values[channel]); - - 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 - } - - return v0 + (v1-v0) * a; - } - } - - return points[points.len()-1].values[channel]; - } - - 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; - } - - 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 render() {} - virtual void render_properties(RECT *toolbox) {} - - virtual void get_size(float *w, float *h) { *w = 0; *h = 0;} - - const char *type_name; - int type; - - bool readonly; - bool visible; -}; - -class LAYERGROUP -{ -public: - array layers; - - int offset_x; - int offset_y; - - int parallax_x; - int parallax_y; - - 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); -}; - -class IMAGE : public IMAGE_INFO -{ -public: - IMAGE() - { - tex_id = -1; - name[0] = 0; - } - - int tex_id; - char name[128]; -}; - -class MAP -{ -public: - array groups; - array images; - array envelopes; - - 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); - } -}; - - -struct PROPERTY -{ - const char *name; - int value; - int type; - int min; - int max; -}; - -enum -{ - PROPTYPE_NULL=0, - PROPTYPE_INT_STEP, - PROPTYPE_INT_SCROLL, - PROPTYPE_COLOR, - - PROPS_NONE=0, - PROPS_GROUP, - PROPS_LAYER, - PROPS_QUAD, - PROPS_QUAD_POINT, -}; - -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; - mouse_delta_x = 0; - mouse_delta_y = 0; - mouse_delta_wx = 0; - mouse_delta_wy = 0; - - gui_active = true; - proof_borders = false; - - - animate = false; - animate_start = 0; - animate_time = 0; - - props = PROPS_NONE; - - show_envelope_editor = 0; - } - - void make_game_group(LAYERGROUP *group); - void make_game_layer(LAYER *layer); - - int save(const char *filename); - int load(const char *filename); - - QUAD *get_selected_quad(); - LAYER *get_selected_layer_type(int index, int type); - LAYER *get_selected_layer(int index); - LAYERGROUP *get_selected_group(); - - class LAYER_GAME *game_layer; - LAYERGROUP *game_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 gui_active; - bool proof_borders; - float mouse_delta_x; - float mouse_delta_y; - float mouse_delta_wx; - float mouse_delta_wy; - - bool animate; - int64 animate_start; - float animate_time; - - int props; - - int show_envelope_editor; - - 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 void render_properties(RECT *toolbox); - - 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 render_properties(RECT *toolbox); - - void get_size(float *w, float *h); - - int image; - array quads; -}; - - -class LAYER_GAME : public LAYER_TILES -{ -public: - LAYER_GAME(int w, int h); - ~LAYER_GAME(); - - virtual void render_properties(RECT *toolbox); -}; - - -inline 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; - } -} - -inline 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(1300*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; -} diff --git a/src/game/g_game.h b/src/game/g_game.h index 70a15a19..0e405260 100644 --- a/src/game/g_game.h +++ b/src/game/g_game.h @@ -1,4 +1,7 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef GAME_GAME_H +#define GAME_GAME_H + #include #include #include @@ -169,3 +172,5 @@ enum ITEM_ARMOR=0x00030001, ITEM_NINJA=0x00040001, }; + +#endif diff --git a/src/game/g_mapitems.h b/src/game/g_mapitems.h new file mode 100644 index 00000000..f6375d02 --- /dev/null +++ b/src/game/g_mapitems.h @@ -0,0 +1,138 @@ +/* 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, +}; + + +// game layer tiles +enum +{ + ENTITY_NULL=0, + ENTITY_SPAWN, + ENTITY_SPAWN_RED, + ENTITY_SPAWN_BLUE, + ENTITY_FLAGSTAND_RED, + ENTITY_FLAGSTAND_BLUE, + ENTITY_ARMOR_1, + ENTITY_HEATH_1, + ENTITY_WEAPON_SHOTGUN, + ENTITY_WEAPON_ROCKET, + ENTITY_POWERUP_NINJA, + NUM_ENTITIES, + + TILE_AIR=0, + TILE_SOLID, + TILE_NOHOOK, + + TILEFLAG_VFLIP=1, + TILEFLAG_HFLIP=2, + + 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 reserved1; + unsigned char reserved2; +} TILE; + +typedef struct +{ + int version; + int width; + int height; + int external; + int image_name; + int image_data; +} MAPITEM_IMAGE; + +typedef struct +{ + int version; + int offset_x; + int offset_y; + int parallax_x; + int parallax_y; + + int start_layer; + int num_layers; +} MAPITEM_GROUP; + +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; + +// float to fixed +inline int f2fx(float v) { return (int)(v*(float)(1<<10)); } +inline float fx2f(int v) { return v*(1.0f/(1<<10)); } + + +#endif diff --git a/src/game/g_mapres_col.h b/src/game/g_mapres_col.h index a2aeaee5..3312ef8e 100644 --- a/src/game/g_mapres_col.h +++ b/src/game/g_mapres_col.h @@ -1,4 +1,8 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef GAME_MAPRES_COL_H +#define GAME_MAPRES_COL_H + + #include struct mapres_collision @@ -13,3 +17,5 @@ int col_check_point(int x, int y); int col_width(); int col_height(); bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out); + +#endif diff --git a/src/game/g_math.h b/src/game/g_math.h index e2cf6bd8..54c23ac9 100644 --- a/src/game/g_math.h +++ b/src/game/g_math.h @@ -2,6 +2,8 @@ #ifndef BASE_MATH_H #define BASE_MATH_H +#include + template inline T clamp(T val, T min, T max) { @@ -22,6 +24,9 @@ inline T mix(const T a, const T b, TB amount) { return a + (b-a)*amount; } + +inline float frandom() { return rand()/(float)(RAND_MAX); } + const float pi = 3.1415926535897932384626433f; template inline T min(T a, T b) { return a