diff --git a/NOTICE b/NOTICE index 6722a5bb0..23d07d6cd 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,3 @@ - argtable3 library ====================== Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann @@ -36,6 +35,15 @@ All rights reserved. This code is derived from software contributed to The NetBSD Foundation by Dieter Baron and Thomas Klausner. +======= + + +fixedptc library +====================== + + +Copyright (c) 2010-2012 Ivan Voras +Copyright (c) 2012 Tim Hartrick Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -46,6 +54,7 @@ are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +<<<<<<< HEAD THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR @@ -168,3 +177,15 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +======= +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/src/controller/ai_controller.c b/src/controller/ai_controller.c index 4c970f3b4..647bd0b31 100644 --- a/src/controller/ai_controller.c +++ b/src/controller/ai_controller.c @@ -391,7 +391,7 @@ int get_enemy_range(const controller *ctrl) { object *o_enemy = game_state_find_object(ctrl->gs, game_state_get_player(ctrl->gs, h->player_id == 1 ? 0 : 1)->har_obj_id); - int range_units = fabsf(o_enemy->pos.x - o->pos.x) / 30; + int range_units = fixedpt_toint(fixedpt_abs(o_enemy->pos.fx - o->pos.fx)) / 30; switch(range_units) { case 0: case 1: @@ -1477,7 +1477,7 @@ void set_selected_move(controller *ctrl, af_move *selected_move) { a->move_str_pos = str_size(&selected_move->move_string) - 1; object *o_enemy = game_state_find_object(ctrl->gs, game_state_get_player(ctrl->gs, h->player_id == 1 ? 0 : 1)->har_obj_id); - a->move_stats[a->selected_move->id].last_dist = fabsf(o->pos.x - o_enemy->pos.x); + a->move_stats[a->selected_move->id].last_dist = fixedpt_toint(fixedpt_abs(o->pos.fx - o_enemy->pos.fx)); a->blocked = 0; // log_debug("AI selected move %s", str_c(&selected_move->move_string)); } @@ -1605,7 +1605,7 @@ int ai_block_har(controller *ctrl, ctrl_event **ev) { har *h_enemy = object_get_userdata(o_enemy); // XXX TODO get maximum move distance from the animation object - if(fabsf(o_enemy->pos.x - o->pos.x) < 100 && h_enemy->executing_move && smart_usually(a)) { + if(fixedpt_abs(o_enemy->pos.fx - o->pos.fx) < fixedpt_fromint(100) && h_enemy->executing_move && smart_usually(a)) { if(har_is_crouching(h_enemy)) { a->cur_act = DOWNBACK; controller_cmd(ctrl, a->cur_act, ev); @@ -1648,7 +1648,7 @@ int ai_block_projectile(controller *ctrl, ctrl_event **ev) { if(object_get_direction(o_prj) == OBJECT_FACE_LEFT) { pos_prj.x = object_get_pos(o_prj).x + ((cur_sprite->pos.x * -1) - size_prj.x); } - if(fabsf(pos_prj.x - o->pos.x) < 120) { + if(abs(pos_prj.x - object_get_pos(o).x) < 120) { a->cur_act = DOWNBACK; controller_cmd(ctrl, a->cur_act, ev); return 1; @@ -1770,9 +1770,9 @@ void handle_movement(controller *ctrl, ctrl_event **ev) { // double jump controller_cmd(ctrl, ACT_DOWN, ev); } - if(o->vel.x < 0) { + if(o->vel.fx < 0) { controller_cmd(ctrl, ACT_UP | ACT_LEFT, ev); - } else if(o->vel.x > 0) { + } else if(o->vel.fx > 0) { controller_cmd(ctrl, ACT_UP | ACT_RIGHT, ev); } else { controller_cmd(ctrl, ACT_UP, ev); diff --git a/src/formats/af.c b/src/formats/af.c index de7287564..cd6d6cdb4 100644 --- a/src/formats/af.c +++ b/src/formats/af.c @@ -116,13 +116,13 @@ int sd_af_load(sd_af_file *af, const char *filename) { // Header af->file_id = sd_read_uword(r); af->exec_window = sd_read_uword(r); // Always 10 - af->endurance = sd_read_udword(r) * 1.0f; + af->endurance = sd_read_dword(r); af->unknown_b = sd_read_ubyte(r); // Always 1 or 2 af->health = sd_read_uword(r); - af->forward_speed = sd_read_dword(r) / 256.0f; - af->reverse_speed = sd_read_dword(r) / 256.0f; - af->jump_speed = sd_read_dword(r) / 256.0f; - af->fall_speed = sd_read_dword(r) / 256.0f; + af->forward_speed = sd_read_dword(r); + af->reverse_speed = sd_read_dword(r); + af->jump_speed = sd_read_dword(r); + af->fall_speed = sd_read_dword(r); af->unknown_c = sd_read_ubyte(r); // Always 0x32 ? af->unknown_d = sd_read_ubyte(r); // Always 0x14 ? diff --git a/src/formats/af.h b/src/formats/af.h index 4cfd9e6af..887bc2d0d 100644 --- a/src/formats/af.h +++ b/src/formats/af.h @@ -20,17 +20,17 @@ * Contains information about a single HAR (combat robot). */ typedef struct { - uint16_t file_id; ///< File ID - uint16_t exec_window; ///< Move execution window (?) - float endurance; ///< HAR Endurance - uint8_t unknown_b; ///< Unknown value - uint16_t health; ///< HAR Health - float forward_speed; ///< HAR fwd speed - float reverse_speed; ///< HAR bwd speed - float jump_speed; ///< HAR jump speed - float fall_speed; ///< HAR fall speed - uint8_t unknown_c; ///< Unknown value - uint8_t unknown_d; ///< Unknown value + uint16_t file_id; ///< File ID + uint16_t exec_window; ///< Move execution window (?) + int32_t endurance; ///< HAR Endurance + uint8_t unknown_b; ///< Unknown value + uint16_t health; ///< HAR Health + int32_t forward_speed; ///< HAR fwd speed + int32_t reverse_speed; ///< HAR bwd speed + int32_t jump_speed; ///< HAR jump speed + int32_t fall_speed; ///< HAR fall speed + uint8_t unknown_c; ///< Unknown value + uint8_t unknown_d; ///< Unknown value sd_move *moves[MAX_AF_MOVES]; ///< All HAR moves. char soundtable[30]; ///< All sounds used by the animations in this HAR file. diff --git a/src/game/game_state.c b/src/game/game_state.c index 35cde72fb..84472ab2f 100644 --- a/src/game/game_state.c +++ b/src/game/game_state.c @@ -81,13 +81,13 @@ int game_state_get_assertion_operand(rec_assertion_operand *op, game_state *gs) har *har = object_get_userdata(obj); switch(op->value.attr.attribute) { case ATTR_X_POS: - return obj->pos.x; + return fixedpt_toint(obj->pos.fx); case ATTR_Y_POS: - return obj->pos.y; + return fixedpt_toint(obj->pos.fy); case ATTR_X_VEL: - return obj->vel.x; + return fixedpt_toint(obj->vel.fx); case ATTR_Y_VEL: - return obj->vel.y; + return fixedpt_toint(obj->vel.fy); case ATTR_STATE_ID: return har->state; case ATTR_ANIMATION_ID: @@ -99,10 +99,10 @@ int game_state_get_assertion_operand(rec_assertion_operand *op, game_state *gs) case ATTR_OPPONENT_DISTANCE: { object *obj_opp = game_state_find_object( gs, game_player_get_har_obj_id(game_state_get_player(gs, abs(op->value.attr.har_id - 1)))); - return fabsf(obj->pos.x - obj_opp->pos.x); - case ATTR_DIRECTION: - return object_get_direction(obj); + return abs(fixedpt_toint(obj->pos.fx - obj_opp->pos.fx)); } + case ATTR_DIRECTION: + return object_get_direction(obj); default: abort(); } @@ -141,16 +141,16 @@ bool game_state_check_assertion_is_met(rec_assertion *ass, game_state *gs) { switch(ass->operand1.value.attr.attribute) { case ATTR_X_POS: - obj->pos.x = operand2; + obj->pos.fx = fixedpt_fromint(operand2); return true; case ATTR_Y_POS: - obj->pos.y = operand2; + obj->pos.fy = fixedpt_fromint(operand2); return true; case ATTR_X_VEL: - obj->vel.x = operand2; + obj->vel.fx = fixedpt_fromint(operand2); return true; case ATTR_Y_VEL: - obj->vel.y = operand2; + obj->vel.fy = fixedpt_fromint(operand2); return true; case ATTR_HEALTH: har->health = operand2; diff --git a/src/game/gui/trn_menu.c b/src/game/gui/trn_menu.c index 428e1edc3..a72cfe8d5 100644 --- a/src/game/gui/trn_menu.c +++ b/src/game/gui/trn_menu.c @@ -165,15 +165,15 @@ static void trnmenu_layout(component *c, int x, int y, int w, int h) { } static vec2f center(component *c) { - return vec2f_create(c->x + c->w / 2, c->y + c->h / 2); + return vec2f_create(fixedpt_fromint(c->x + c->w) / 2, fixedpt_fromint(c->y + c->h) / 2); } static vec2f rcenter(component *c) { - return vec2f_create(c->x + c->w, c->y + c->h / 2); + return vec2f_create(fixedpt_fromint(c->x + c->w), fixedpt_fromint(c->y + c->h) / 2); } static vec2f lcenter(component *c) { - return vec2f_create(c->x, c->y + c->h / 2); + return vec2f_create(fixedpt_fromint(c->x), fixedpt_fromint(c->y + c->h) / 2); } static int find_next_button(component *c, int act) { @@ -184,7 +184,7 @@ static int find_next_button(component *c, int act) { sizer_begin_iterator(c, &it); component *cur = sizer_get(c, m->selected); - float best_dist = 9999.0f; + fixedpt best_dist = FIXEDPT_MAX; int best_idx = -1; int idx_now = 0; foreach(it, tmp) { @@ -196,7 +196,7 @@ static int find_next_button(component *c, int act) { switch(act) { case ACT_LEFT: if(t->x < cur->x) { - float tdist = vec2f_dist(rcenter(t), lcenter(cur)); + fixedpt tdist = vec2f_dist(rcenter(t), lcenter(cur)); if(tdist < best_dist) { best_dist = tdist; best_idx = idx_now; @@ -205,7 +205,7 @@ static int find_next_button(component *c, int act) { break; case ACT_RIGHT: if(t->x > cur->x) { - float tdist = vec2f_dist(lcenter(t), rcenter(cur)); + fixedpt tdist = vec2f_dist(lcenter(t), rcenter(cur)); if(tdist < best_dist) { best_dist = tdist; best_idx = idx_now; @@ -214,7 +214,7 @@ static int find_next_button(component *c, int act) { break; case ACT_UP: if(t->y < cur->y) { - float tdist = vec2f_dist(center(t), center(cur)); + fixedpt tdist = vec2f_dist(center(t), center(cur)); if(tdist < best_dist) { best_dist = tdist; best_idx = idx_now; @@ -223,7 +223,7 @@ static int find_next_button(component *c, int act) { break; case ACT_DOWN: if(t->y > cur->y) { - float tdist = vec2f_dist(center(t), center(cur)); + fixedpt tdist = vec2f_dist(center(t), center(cur)); if(tdist < best_dist) { best_dist = tdist; best_idx = idx_now; diff --git a/src/game/objects/arena_constraints.h b/src/game/objects/arena_constraints.h index 24e13b932..11216d442 100644 --- a/src/game/objects/arena_constraints.h +++ b/src/game/objects/arena_constraints.h @@ -5,4 +5,9 @@ #define ARENA_RIGHT_WALL 300 #define ARENA_FLOOR 190 +// fixed point versions +#define ARENA_LEFT_WALLF fixedpt_fromint(ARENA_LEFT_WALL) +#define ARENA_RIGHT_WALLF fixedpt_fromint(ARENA_RIGHT_WALL) +#define ARENA_FLOORF fixedpt_fromint(ARENA_FLOOR) + #endif // ARENA_CONSTRAINTS_H diff --git a/src/game/objects/har.c b/src/game/objects/har.c index 0e8613f41..1d2a9d811 100644 --- a/src/game/objects/har.c +++ b/src/game/objects/har.c @@ -26,7 +26,7 @@ #include "video/vga_state.h" #include "video/video.h" -#define IS_ZERO(n) (n < 0.8 && n > -0.8) +#define IS_ZERO(n) (n < fixedpt_rconst(0.8) && n > fixedpt_rconst(-0.8)) void har_finished(object *obj); int har_act(object *obj, int act_type); @@ -306,8 +306,10 @@ void har_walk_to(object *obj, int destination) { h->walk_done_anim = obj->cur_animation->id; - float vx = h->fwd_speed * object_get_direction(obj); - log_debug("set velocity to %f", vx); + fixedpt vx = h->fwd_speedf * object_get_direction(obj); + char vx_buf[FIXEDPT_STR_BUFSIZE]; + fixedpt_str(vx, vx_buf, sizeof(vx_buf), -1); + log_debug("set velocity to %s", vx_buf); object_set_vel(obj, vec2f_create(vx, 0)); object_set_animation(obj, &move->ani); @@ -410,7 +412,7 @@ void cb_har_spawn_object(object *parent, int id, vec2i pos, vec2f vel, uint8_t m object_create(obj, parent->gs, pos, vel); object_set_stl(obj, object_get_stl(parent)); object_set_animation(obj, &move->ani); - object_set_gravity(obj, g / 100.0f); + object_set_gravity(obj, fixedpt_fromint(g) / 100); object_set_pal_offset(obj, object_get_pal_offset(parent)); object_set_pal_limit(obj, object_get_pal_limit(parent)); // Set all projectiles to their own layer + har layer @@ -453,7 +455,8 @@ void har_floor_landing_effects(object *obj, bool play_sound) { int amount = rand_int(2) + 1; for(int i = 0; i < amount; i++) { int variance = rand_int(20) - 10; - vec2i coord = vec2i_create(obj->pos.x + variance + i * 10, obj->pos.y); + vec2i coord = vec2f_to_i(obj->pos); + coord.x += variance + i * 10; object *dust = omf_calloc(1, sizeof(object)); object_create(dust, obj->gs, coord, vec2f_create(0, 0)); object_set_stl(dust, object_get_stl(obj)); @@ -463,7 +466,7 @@ void har_floor_landing_effects(object *obj, bool play_sound) { // Landing sound if(play_sound) { - float pos_pan = ((float)obj->pos.x - 160.0f) / 160.0f; + float pos_pan = (fixedpt_tofloat(obj->pos.fx) - 160.0f) / 160.0f; game_state_play_sound(obj->gs, 56, 0.3f, pos_pan, 2.2f); } } @@ -479,17 +482,17 @@ void har_move(object *obj) { return; } - obj->pos.x += obj->vel.x; - obj->pos.y += obj->vel.y; + obj->pos.fx += obj->vel.fx; + obj->pos.fy += obj->vel.fy; object *enemy_obj = game_state_find_object(obj->gs, game_player_get_har_obj_id(game_state_get_player(obj->gs, !h->player_id))); har *enemy_har = object_get_userdata(enemy_obj); if(h->walk_destination > 0 && h->walk_done_anim && - ((obj->pos.x >= h->walk_destination && object_get_direction(obj) == OBJECT_FACE_RIGHT) || - (obj->pos.x <= h->walk_destination && object_get_direction(obj) == OBJECT_FACE_LEFT))) { - obj->pos.x = h->walk_destination; + ((obj->pos.fx >= fixedpt_fromint(h->walk_destination) && object_get_direction(obj) == OBJECT_FACE_RIGHT) || + (obj->pos.fx <= fixedpt_fromint(h->walk_destination) && object_get_direction(obj) == OBJECT_FACE_LEFT))) { + obj->pos.fx = fixedpt_fromint(h->walk_destination); log_debug("reached destination!"); if(obj->animation_state.shadow_corner_hack) { object_set_direction(obj, object_get_direction(obj) * -1); @@ -508,11 +511,13 @@ void har_move(object *obj) { h->walk_done_anim = 0; return; } else if(h->walk_destination > 0) { - log_debug("still walking to %d, at %f", h->walk_destination, obj->pos.x); + char posx_buf[FIXEDPT_STR_BUFSIZE]; + fixedpt_str(obj->pos.fx, posx_buf, sizeof(posx_buf), -1); + log_debug("still walking to %d, at %s", h->walk_destination, posx_buf); } // Check for wall hits - if(obj->pos.x <= ARENA_LEFT_WALL || obj->pos.x >= ARENA_RIGHT_WALL) { + if(obj->pos.fx <= ARENA_LEFT_WALLF || obj->pos.fx >= ARENA_RIGHT_WALLF) { h->is_wallhugging = 1; if(player_frame_isset(obj, "cw") && player_frame_isset(obj, "d")) { log_debug("disabling d tag on animation because of wall hit"); @@ -524,10 +529,10 @@ void har_move(object *obj) { } // Handle floor collisions - if(obj->pos.y >= ARENA_FLOOR) { + if(obj->pos.fy >= ARENA_FLOORF) { controller *ctrl = game_player_get_ctrl(game_state_get_player(obj->gs, h->player_id)); - obj->pos.y = ARENA_FLOOR; + obj->pos.fy = ARENA_FLOORF; char last_input = get_last_input(h); // Change animation from jump to walk or idle, @@ -548,12 +553,12 @@ void har_move(object *obj) { } else if(last_input == '7' || last_input == '8' || last_input == '9') { har_set_ani(obj, ANIM_JUMPING, 0); h->state = STATE_JUMPING; - float vx = 0.0f; - float vy = h->jump_speed; + fixedpt vx = 0; + fixedpt vy = h->jump_speed; int jump_dir = 0; int direction = object_get_direction(obj); if(last_input == '9') { - vx = (h->fwd_speed * direction); + vx = (h->fwd_speedf * direction); object_set_tick_pos(obj, 110); object_set_stride(obj, 7); // Pass 7 frames per tick jump_dir = 1; @@ -562,7 +567,7 @@ void har_move(object *obj) { // at -100 frames (seems to be about right) object_set_playback_direction(obj, PLAY_BACKWARDS); object_set_tick_pos(obj, -110); - vx = (h->back_speed * direction * -1); + vx = (h->back_speedf * direction * -1); object_set_stride(obj, 7); // Pass 7 frames per tick jump_dir = -1; } else { @@ -588,12 +593,12 @@ void har_move(object *obj) { har_face_enemy(obj, enemy_obj); } } else if(h->state == STATE_FALLEN || h->state == STATE_RECOIL) { - if(obj->vel.y > 0) { + if(obj->vel.fy > 0) { // bounce and screenshake if falling fast enough - if(obj->vel.y > 6) { + if(obj->vel.fy > fixedpt_fromint(6)) { har_floor_landing_effects(obj, false); - obj->vel.y = -3; - obj->vel.x = obj->vel.x / 2; + obj->vel.fy = fixedpt_fromint(-3); + obj->vel.fx = obj->vel.fx / 2; if(h->id != 10) { object_set_custom_string(obj, "l20s4sp13zzN3-zzM100"); obj->gs->screen_shake_vertical = 5; // Multiplied by 5 to make it visible @@ -603,16 +608,16 @@ void har_move(object *obj) { obj->gs->screen_shake_vertical = 15; // Multiplied by 5 to make it visible } } else { - obj->vel.y = 0; - obj->vel.x = 0; + obj->vel.fy = 0; + obj->vel.fx = 0; } } - if(obj->pos.x < ARENA_LEFT_WALL) { - obj->pos.x = ARENA_LEFT_WALL; + if(obj->pos.fx < ARENA_LEFT_WALLF) { + obj->pos.fx = ARENA_LEFT_WALLF; } - if(obj->pos.x > ARENA_RIGHT_WALL) { - obj->pos.x = ARENA_RIGHT_WALL; + if(obj->pos.fx > ARENA_RIGHT_WALLF) { + obj->pos.fx = ARENA_RIGHT_WALLF; } // prevent har from sliding after defeat, unless they're 'fallen' @@ -620,7 +625,8 @@ void har_move(object *obj) { h->state = STATE_DEFEAT; har_set_ani(obj, h->custom_defeat_animation ? h->custom_defeat_animation : ANIM_DEFEAT, 0); - } else if(obj->pos.y >= (ARENA_FLOOR - 5) && IS_ZERO(obj->vel.x) && player_is_last_frame(obj)) { + } else if(obj->pos.fy >= (ARENA_FLOORF - fixedpt_fromint(5)) && IS_ZERO(obj->vel.fx) && + player_is_last_frame(obj)) { if(h->state == STATE_FALLEN) { if(h->health <= 0) { // fallen, but done bouncing @@ -642,40 +648,40 @@ void har_move(object *obj) { // add some friction from the floor if we're not walking during scrap // This is important to dampen/eliminate the velocity added from pushing away from the other HAR // friction decreases velocity by 1 each tick, and sets it to 0 if its under |2| - if(obj->vel.x > 0.0f) { - if(obj->vel.x < 2.0f) { - obj->vel.x = 0.0f; + if(obj->vel.fx > 0) { + if(obj->vel.fx < fixedpt_fromint(2)) { + obj->vel.fx = 0; } else { - obj->vel.x -= 1.0f; + obj->vel.fx -= fixedpt_fromint(1); } - } else if(obj->vel.x < 0.0f) { - if(obj->vel.x > -2.0f) { - obj->vel.x = 0.0f; + } else if(obj->vel.fx < 0) { + if(obj->vel.fx > fixedpt_fromint(-2)) { + obj->vel.fx = 0; } else { - obj->vel.x += 1.0f; + obj->vel.fx += fixedpt_fromint(1); } } } if(h->state == STATE_WALKTO) { har_face_enemy(obj, enemy_obj); - obj->pos.x += (h->fwd_speed * object_get_direction(obj)) * (h->hard_close ? 0.0 : 1.0); + obj->pos.fx += (h->fwd_speedf * object_get_direction(obj)) * (h->hard_close ? 0 : 1); } else if(h->state == STATE_WALKFROM) { har_face_enemy(obj, enemy_obj); - obj->pos.x -= (h->back_speed * object_get_direction(obj)) * (h->hard_close ? 0.5 : 1.0); + obj->pos.fx -= (h->back_speedf * object_get_direction(obj)) / (h->hard_close ? 2 : 1); } object_apply_controllable_velocity(obj, false, last_input); } else { - obj->vel.y += obj->gravity; + obj->vel.fy += obj->gravity; // Terminal Velocity - if(obj->vel.y > 13) { - obj->vel.y = 13; + if(obj->vel.fy > fixedpt_fromint(13)) { + obj->vel.fy = fixedpt_fromint(13); } } } -void har_take_damage(object *obj, const str *string, float damage, float stun) { +static void har_take_damage(object *obj, const str *string, int damage, int32_t stun) { har *h = object_get_userdata(obj); if(h->state == STATE_VICTORY || h->state == STATE_DONE) { @@ -709,9 +715,10 @@ void har_take_damage(object *obj, const str *string, float damage, float stun) { if(player->pilot->photo) { // in tournament mode, damage is mitigated by armor // (Armor + 2.5) * .25 - log_debug("applying %f to %d modulated by armor %f", damage, h->health, - 0.25f * (2.5f + player->pilot->armor)); - h->health -= damage / (0.25f * (2.5f + player->pilot->armor)); + int armor_numer = (5 + 2 * player->pilot->armor); + int armor_denom = 8; + log_debug("applying %f to %d modulated by armor %f", damage, h->health, armor_numer / (float)armor_denom); + h->health -= damage * armor_denom / armor_numer; } else { h->health -= damage; } @@ -720,19 +727,19 @@ void har_take_damage(object *obj, const str *string, float damage, float stun) { // Handle health changes if(h->health <= 0) { h->health = 0; - h->endurance = 0.0f; + h->endurance = 0; } if(!h->throw_duration) { - log_debug("applying %f stun damage to %f", stun, h->endurance); + log_debug("applying %d stun damage to %d", stun, h->endurance); h->endurance -= stun; } - if(h->endurance < 1.0f) { + if(h->endurance < 1) { if(h->state == STATE_STUNNED) { // refill endurance h->endurance = h->endurance_max; } else { - h->endurance = 0.0f; + h->endurance = 0; } } @@ -792,12 +799,13 @@ void har_take_damage(object *obj, const str *string, float damage, float stun) { str_append_c(&custom, "-L2-M5-L2"); } - obj->vel.y = obj->vertical_velocity_modifier * ((((30.0f - damage) * 0.133333f) + 6.5f) * -1.0); + obj->vel.fy = -fixedpt_xmul((30 - damage) * fixedpt_rconst(0.133333) + fixedpt_rconst(6.5), + obj->vertical_velocity_modifierf); // TODO there's an alternative formula used in some conditions: // (((damage * 0.09523809523809523) + 3.5) * -1) * obj->vertical_velocity_modifier // but we don't know what those conditions are - obj->vel.x = - (((damage * 0.16666666f) + 2.0f) * object_get_direction(obj) * -1) * obj->horizontal_velocity_modifier; + obj->vel.fx = -fixedpt_xmul3(damage * fixedpt_rconst(0.16666666) + fixedpt_fromint(2), + object_get_direction(obj), obj->horizontal_velocity_modifierf); h->state = STATE_FALLEN; object_set_stride(obj, 1); } @@ -817,26 +825,33 @@ void har_take_damage(object *obj, const str *string, float damage, float stun) { // Insanius 3/17/2025 - This is actually mostly correct, the OG checks if the string starts with the 'k' char const sd_script_frame *frame = sd_script_get_frame(&obj->animation_state.parser, 0); if(frame != NULL && sd_script_isset(frame, "k")) { - obj->vel.x = -5 * object_get_direction(obj); - obj->vel.y = -8; + obj->vel.fx = fixedpt_fromint(-5) * object_get_direction(obj); + obj->vel.fy = fixedpt_fromint(-8); } } } -void har_spawn_oil(object *obj, vec2i pos, int amount, float gravity, int layer) { +void har_spawn_oil(object *obj, vec2i pos, int amount, fixedpt gravity, int layer) { har *h = object_get_userdata(obj); // burning oil for(int i = 0; i < amount; i++) { // Calculate velocity etc. - float rv = rand_int(100) / 100.0f - 0.5; - float velx = (5 * cosf(90 + i - (amount) / 2 + rv)) * object_get_direction(obj); - float vely = -12 * sinf(i / amount + rv); +#if 0 + float fvelx = (5 * cosf(90 + i - (amount) / 2 + frv)) * object_get_direction(obj); + float fvely = -12 * sinf(i / amount + frv); + fixedpt velx = fixedpt_rconst(fvelx); + fixedpt vely = fixedpt_rconst(fvely); +#else + fixedpt rv = rand_fixedpt(FIXEDPT_ONE) - FIXEDPT_ONE_HALF; + fixedpt velx = (5 * -fixedpt_cos(fixedpt_fromint(i - (amount) / 2) + rv)) * object_get_direction(obj); + fixedpt vely = -12 * fixedpt_sin(rv); +#endif // Make sure the oil drops have somekind of velocity // (to prevent floating scrap objects) - if(vely < 0.1f && vely > -0.1f) - vely += 0.21f; + if(vely < fixedpt_rconst(0.1) && vely > fixedpt_rconst(-0.1)) + vely += fixedpt_rconst(0.21); // Create the object object *scrap = omf_calloc(1, sizeof(object)); @@ -865,7 +880,7 @@ void har_spawn_scrap(object *obj, vec2i pos, int amount) { // wild ass guess int oil_amount = amount / 3; har *h = object_get_userdata(obj); - har_spawn_oil(obj, pos, oil_amount, 1, RENDER_LAYER_TOP); + har_spawn_oil(obj, pos, oil_amount, FIXEDPT_ONE, RENDER_LAYER_TOP); // scrap metal // TODO this assumes the default scrap level and does not consider BIG[1-9] @@ -903,7 +918,7 @@ void har_spawn_scrap(object *obj, vec2i pos, int amount) { object_create(scrap, obj->gs, pos, vec2f_create(velx, vely)); object_set_animation(scrap, &af_get_move(h->af_data, anim_no)->ani); object_set_stl(scrap, object_get_stl(obj)); - object_set_gravity(scrap, 1); + object_set_gravity(scrap, fixedpt_fromint(1)); object_set_pal_offset(scrap, object_get_pal_offset(obj)); object_set_pal_limit(obj, object_get_pal_limit(obj)); object_set_layers(scrap, LAYER_SCRAP); @@ -1027,6 +1042,8 @@ void har_check_closeness(object *obj_a, object *obj_b) { sprite *sprite_a = animation_get_sprite(obj_a->cur_animation, obj_a->cur_sprite_id); sprite *sprite_b = animation_get_sprite(obj_b->cur_animation, obj_b->cur_sprite_id); int hard_limit = 32; // Push opponent if HARs too close. Harrison-Stetson method value. + fixedpt hard_limitf = + fixedpt_fromint(hard_limit); // Push opponent if HARs too close. Harrison-Stetson method value. // TODO verify throw distance soft limit int soft_limit = 45; // Sets HAR A as being close to HAR B if closer than this. This should be affected by throw // distance setting. @@ -1072,49 +1089,50 @@ void har_check_closeness(object *obj_a, object *obj_b) { b->hard_close = 1; } - float a_speed = (a->fwd_speed * object_get_direction(obj_a)) * (a->hard_close ? 0.5 : 1.0); - float b_speed = (b->fwd_speed * object_get_direction(obj_b)) * (b->hard_close ? 0.5 : 1.0); + fixedpt a_speedf = (a->fwd_speedf * object_get_direction(obj_a)) / (a->hard_close ? 2 : 1); + fixedpt b_speedf = (b->fwd_speedf * object_get_direction(obj_b)) / (b->hard_close ? 2 : 1); bool pushed = true; // If HARs get too close together, handle it if(a->state == STATE_WALKTO && b->state == STATE_WALKTO && a->hard_close) { // both hars are walking into each other, figure out the resulting vector and apply it - obj_b->pos.x += b_speed + a_speed; - obj_a->pos.x += a_speed + b_speed; + obj_b->pos.fx += b_speedf + a_speedf; + obj_a->pos.fx += a_speedf + b_speedf; } else if(a->state == STATE_WALKTO && a->hard_close) { // A pushes B - obj_b->pos.x += a_speed; + obj_b->pos.fx += a_speedf; } else if(b->state == STATE_WALKTO && b->hard_close) { // B pushes A - obj_a->pos.x += b_speed; + obj_a->pos.fx += b_speedf; } else { pushed = false; } - if(fabsf(obj_a->pos.x - obj_b->pos.x) < hard_limit && a->state != STATE_JUMPING && b->state != STATE_JUMPING) { - if(obj_a->pos.x <= ARENA_LEFT_WALL) { - obj_a->pos.x = ARENA_LEFT_WALL; - obj_b->pos.x = ARENA_LEFT_WALL + hard_limit; - } else if(obj_b->pos.x <= ARENA_LEFT_WALL) { - obj_b->pos.x = ARENA_LEFT_WALL; - obj_a->pos.x = ARENA_LEFT_WALL + hard_limit; - } else if(obj_a->pos.x >= ARENA_RIGHT_WALL) { - obj_a->pos.x = ARENA_RIGHT_WALL; - obj_b->pos.x = ARENA_RIGHT_WALL - hard_limit; - } else if(obj_b->pos.x >= ARENA_RIGHT_WALL) { - obj_b->pos.x = ARENA_RIGHT_WALL; - obj_a->pos.x = ARENA_RIGHT_WALL - hard_limit; + if(fixedpt_abs(obj_a->pos.fx - obj_b->pos.fx) < hard_limitf && a->state != STATE_JUMPING && + b->state != STATE_JUMPING) { + if(obj_a->pos.fx <= ARENA_LEFT_WALLF) { + obj_a->pos.fx = ARENA_LEFT_WALLF; + obj_b->pos.fx = ARENA_LEFT_WALLF + hard_limitf; + } else if(obj_b->pos.fx <= ARENA_LEFT_WALLF) { + obj_b->pos.fx = ARENA_LEFT_WALLF; + obj_a->pos.fx = ARENA_LEFT_WALLF + hard_limitf; + } else if(obj_a->pos.fx >= ARENA_RIGHT_WALLF) { + obj_a->pos.fx = ARENA_RIGHT_WALLF; + obj_b->pos.fx = ARENA_RIGHT_WALLF - hard_limitf; + } else if(obj_b->pos.fx >= ARENA_RIGHT_WALLF) { + obj_b->pos.fx = ARENA_RIGHT_WALLF; + obj_a->pos.fx = ARENA_RIGHT_WALLF - hard_limitf; } else if(!pushed) { // they're simply too close - float distance = hard_limit - fabsf(obj_a->pos.x - obj_b->pos.x); - if(obj_a->pos.x < obj_b->pos.x) { - obj_a->pos.x -= distance / 2.0; - obj_a->pos.x = clampf(obj_a->pos.x, ARENA_LEFT_WALL, ARENA_RIGHT_WALL); - obj_b->pos.x = obj_a->pos.x + hard_limit; + fixedpt distancef = hard_limitf - fixedpt_abs(obj_a->pos.fx - obj_b->pos.fx); + if(obj_a->pos.fx < obj_b->pos.fx) { + obj_a->pos.fx -= distancef / 2; + obj_a->pos.fx = fixedpt_clamp(obj_a->pos.fx, ARENA_LEFT_WALLF, ARENA_RIGHT_WALLF); + obj_b->pos.fx = obj_a->pos.fx + hard_limitf; } else { - obj_a->pos.x += distance / 2.0; - obj_a->pos.x = clampf(obj_a->pos.x, ARENA_LEFT_WALL, ARENA_RIGHT_WALL); - obj_b->pos.x = obj_a->pos.x - hard_limit; + obj_a->pos.fx += distancef / 2; + obj_a->pos.fx = fixedpt_clamp(obj_a->pos.fx, ARENA_LEFT_WALLF, ARENA_RIGHT_WALLF); + obj_b->pos.fx = obj_a->pos.fx - hard_limitf; } } } @@ -1216,8 +1234,10 @@ int har_collide_with_har(object *obj_a, object *obj_b, int loop) { // rehit mode is on, but the opponent isn't airborne or stunned if(obj_b->gs->match_settings.rehit && b->state == STATE_FALLEN && (!object_is_airborne(obj_b) || b->endurance <= 0)) { - log_debug("REHIT is not possible %d %f %f %f", object_is_airborne(obj_b), obj_b->pos.x, obj_b->pos.y, - b->endurance); + char xbuf[FIXEDPT_STR_BUFSIZE], ybuf[FIXEDPT_STR_BUFSIZE]; + fixedpt_str(obj_b->pos.fx, xbuf, sizeof(xbuf), -1); + fixedpt_str(obj_b->pos.fy, ybuf, sizeof(ybuf), -1); + log_debug("REHIT is not possible %d %s %s %d", object_is_airborne(obj_b), xbuf, ybuf, b->endurance); return 0; } @@ -1255,19 +1275,24 @@ int har_collide_with_har(object *obj_a, object *obj_b, int loop) { if(player_frame_isset(obj_a, "i") && move->next_move) { har_set_ani(obj_a, move->next_move, 0); } + char pushbuf[FIXEDPT_STR_BUFSIZE]; if(b->is_wallhugging) { - vec2f push = object_get_vel(obj_a); + vec2f vel = object_get_vel(obj_a); // TODO use 90% of the block pushback as cornerpush for now - push.x = -1 * object_get_direction(obj_a) * (((move->block_stun - 2) * 0.74) + 1) * 0.9; - log_debug("doing block cornerpush of %f", - -1 * object_get_direction(obj_a) * (((move->block_stun - 2) * 0.74) + 1) * 0.9); - object_set_vel(obj_a, push); + fixedpt push = -1 * object_get_direction(obj_a) * + ((move->block_stun - 2) * fixedpt_rconst(0.74 * 0.9) + fixedpt_rconst(0.9)); + fixedpt_str(push, pushbuf, sizeof(pushbuf), -1); + log_debug("doing block cornerpush of %s", pushbuf); + vel.fx += push; + object_set_vel(obj_a, vel); } else { - vec2f push = object_get_vel(obj_b); - push.x = -1 * object_get_direction(obj_b) * (((move->block_stun - 2) * 0.74) + 1); - log_debug("doing block pushback of %f", - -1 * object_get_direction(obj_b) * (((move->block_stun - 2) * 0.74) + 1)); - object_set_vel(obj_b, push); + vec2f vel = object_get_vel(obj_b); + fixedpt push = -1 * object_get_direction(obj_b) * + ((move->block_stun - 2) * fixedpt_rconst(0.74) + fixedpt_rconst(1)); + fixedpt_str(push, pushbuf, sizeof(pushbuf), -1); + log_debug("doing block pushback of %s", pushbuf); + vel.fx += push; + object_set_vel(obj_b, vel); } return 0; } @@ -1312,28 +1337,28 @@ int har_collide_with_har(object *obj_a, object *obj_b, int loop) { if(b->is_wallhugging) { // back the attacker off a little vec2f push = object_get_vel(obj_a); - if(fabsf(push.x) < 5.5f) { + if(fixedpt_abs(push.fx) < fixedpt_rconst(5.5)) { // TODO need real formula here log_debug("doing corner push of 6.3"); - push.x = -6.3f * object_get_direction(obj_a); + push.fx = fixedpt_rconst(-6.3) * object_get_direction(obj_a); object_set_vel(obj_a, push); } } else { vec2f push = object_get_vel(obj_b); - if(fabsf(push.x) < 7.0f) { + if(fixedpt_abs(push.fx) < fixedpt_rconst(7.0)) { log_debug("doing knockback of 7"); - push.x = -7.0f * object_get_direction(obj_b); + push.fx = fixedpt_rconst(-7.0) * object_get_direction(obj_b); object_set_vel(obj_b, push); } } } // rehits only do 60% damage - int damage = rehit ? move->damage * 0.6 : move->damage; + int damage = rehit ? (6 * (int)move->damage) / 10 : move->damage; if(object_is_airborne(obj_a) && object_is_airborne(obj_b)) { // modify the horizontal velocity of the attacker when doing air knockback - obj_a->vel.x *= 0.7f; + obj_a->vel.fx = fixedpt_xmul(obj_a->vel.fx, fixedpt_rconst(0.7)); // the opponent's velocity is modified in har_take_damage } @@ -1350,14 +1375,14 @@ int har_collide_with_har(object *obj_a, object *obj_b, int loop) { str_from_c(&str, "A1-s01l50B2-C2-L5-M400"); har_take_damage(obj_b, &str, damage, move->stun); str_free(&str); - obj_b->vel.x = -5.0 * object_get_direction(obj_b); - obj_b->vel.y = -9.0; + obj_b->vel.fx = fixedpt_fromint(-5) * object_get_direction(obj_b); + obj_b->vel.fy = fixedpt_fromint(-9); } else { har_take_damage(obj_b, &move->footer_string, damage, move->stun); } if(rehit) { - obj_b->vel.y -= 3; + obj_b->vel.fy -= fixedpt_fromint(3); b->rehits[strlen(b->rehits)] = move->id; } else { memset(b->rehits, 0, sizeof(b->rehits)); @@ -1425,8 +1450,10 @@ void har_collide_with_projectile(object *o_har, object *o_pjt) { // rehit mode is on, but the opponent isn't airborne or stunned if(o_har->gs->match_settings.rehit && h->state == STATE_FALLEN && (!object_is_airborne(o_har) || h->endurance <= 0)) { - log_debug("REHIT is not possible %d %f %f %f", object_is_airborne(o_har), o_har->pos.x, o_har->pos.y, - h->endurance); + char xbuf[FIXEDPT_STR_BUFSIZE], ybuf[FIXEDPT_STR_BUFSIZE]; + fixedpt_str(o_har->pos.fx, xbuf, sizeof(xbuf), -1); + fixedpt_str(o_har->pos.fy, ybuf, sizeof(ybuf), -1); + log_debug("REHIT is not possible %d %s %s %d", object_is_airborne(o_har), xbuf, ybuf, h->endurance); return; } @@ -1499,24 +1526,24 @@ void har_collide_with_projectile(object *o_har, object *o_pjt) { // face B to the direction they're being attacked from object_set_direction(o_har, -object_get_direction(o_pjt)); - int damage = rehit ? move->damage * 0.6 : move->damage; + int damage = rehit ? move->damage * 6 / 10 : move->damage; if(player_frame_isset(o_pjt, "ai")) { str str; str_from_c(&str, "A1-s01l50B2-C2-L5-M400"); har_take_damage(o_har, &str, damage, move->stun); str_free(&str); - o_har->vel.x = -5.0 * object_get_direction(o_har); - o_har->vel.y = -9.0; + o_har->vel.fx = fixedpt_fromint(-5) * object_get_direction(o_har); + o_har->vel.fy = fixedpt_fromint(-9); } else { har_take_damage(o_har, &move->footer_string, damage, move->stun); if(rehit) { - o_har->vel.y -= 3; + o_har->vel.fy -= fixedpt_fromint(3); } if(!h->is_wallhugging && !object_is_airborne(o_har)) { vec2f push = object_get_vel(o_har); - if(fabsf(push.x) < 7.0f) { + if(fixedpt_abs(push.fx) < fixedpt_fromint(7)) { log_debug("doing knockback of 7"); - push.x = -7.0f * object_get_direction(o_har); + push.fx = fixedpt_fromint(-7) * object_get_direction(o_har); object_set_vel(o_har, push); } } @@ -1724,16 +1751,16 @@ void har_tick(object *obj) { // Make sure HAR doesn't walk through walls // TODO: Roof! - vec2i pos = object_get_pos(obj); + vec2f pos = obj->pos; int ab_flag = player_frame_isset(obj, "ab"); if(h->state != STATE_DEFEAT && !ab_flag) { int wall_flag = player_frame_isset(obj, "aw"); int wall = 0; - if(pos.x < ARENA_LEFT_WALL) { - pos.x = ARENA_LEFT_WALL; + if(pos.fx < ARENA_LEFT_WALLF) { + pos.fx = ARENA_LEFT_WALLF; obj->wall_collision = true; - } else if(pos.x > ARENA_RIGHT_WALL) { - pos.x = ARENA_RIGHT_WALL; + } else if(pos.fx > ARENA_RIGHT_WALLF) { + pos.fx = ARENA_RIGHT_WALLF; wall = 1; obj->wall_collision = true; } @@ -1746,7 +1773,7 @@ void har_tick(object *obj) { har_set_ani(obj, move->next_move, 0); h->executing_move = 1; } else { - object_set_pos(obj, pos); + obj->pos = pos; har_event_hit_wall(h, wall, ctrl); } } @@ -1780,7 +1807,7 @@ void har_tick(object *obj) { har_event_done(h, ctrl); } - if(pos.y < ARENA_FLOOR && h->state == STATE_RECOIL) { + if(pos.fy < ARENA_FLOORF && h->state == STATE_RECOIL) { log_debug("switching to fallen"); h->state = STATE_FALLEN; har_event_recover(h, ctrl); @@ -1791,7 +1818,7 @@ void har_tick(object *obj) { if(h->stun_timer % 10 == 0) { vec2i pos = object_get_pos(obj); pos.y -= 60; - har_spawn_oil(obj, pos, 5, 0.5f, RENDER_LAYER_BOTTOM); + har_spawn_oil(obj, pos, 5, fixedpt_rconst(0.5), RENDER_LAYER_BOTTOM); } if(h->stun_timer > 100) { har_stunned_done(obj); @@ -1805,7 +1832,7 @@ void har_tick(object *obj) { if(h->health > 0 && h->endurance < h->endurance_max && !(h->executing_move || h->state == STATE_RECOIL || h->state == STATE_STUNNED || h->state == STATE_FALLEN || h->state == STATE_STANDING_UP || h->state == STATE_DEFEAT)) { - h->endurance += 0.0025f * h->endurance_max; // made up but plausible number + h->endurance += h->endurance_max / 400; // made up but plausible number } // Leave shadow trail @@ -2144,8 +2171,8 @@ int har_act(object *obj, int act_type) { // If animation is scrap or destruction, then remove our customizations // from gravity/fall speed, and just use the HARs native value. if(move->category == CAT_SCRAP || move->category == CAT_DESTRUCTION) { - obj->horizontal_velocity_modifier = 1.0f; - obj->vertical_velocity_modifier = 1.0f; + obj->horizontal_velocity_modifierf = FIXEDPT_ONE; + obj->vertical_velocity_modifierf = FIXEDPT_ONE; object_set_gravity(obj, h->af_data->fall_speed); object_set_gravity(enemy_obj, enemy_har->af_data->fall_speed); } @@ -2184,7 +2211,7 @@ int har_act(object *obj, int act_type) { // Don't allow new movement while we're still executing a move if(h->executing_move) { - if(obj->pos.y < ARENA_FLOOR) { + if(obj->pos.fy < ARENA_FLOORF) { // XXX I think 'i' is for 'not interruptable' // XXX I think this is wrong, so comment it out for now if(h->state < STATE_JUMPING /*&& !player_frame_isset(obj, "i")*/) { @@ -2238,7 +2265,6 @@ int har_act(object *obj, int act_type) { return 0; } - float vx, vy; // no moves matched, do player movement int newstate; if((newstate = maybe_har_change_state(h->state, direction, last_input))) { @@ -2256,7 +2282,7 @@ int har_act(object *obj, int act_type) { har_set_ani(obj, ANIM_IDLE, 1); object_set_stride(obj, h->stride); object_set_vel(obj, vec2f_create(0, 0)); - obj->slide_state.vel.x = 0; + obj->slide_state.vel.fx = 0; break; case STATE_WALKTO: har_set_ani(obj, ANIM_WALKING, 1); @@ -2268,13 +2294,13 @@ int har_act(object *obj, int act_type) { object_set_stride(obj, h->stride); har_event_walk(h, -1, ctrl); break; - case STATE_JUMPING: + case STATE_JUMPING: { har_set_ani(obj, ANIM_JUMPING, 0); - vx = 0.0f; - vy = h->jump_speed; + fixedpt vx = 0; + fixedpt vy = h->jump_speed; int jump_dir = 0; if(last_input == '9') { - vx = (h->fwd_speed * direction); + vx = (h->fwd_speedf * direction); object_set_tick_pos(obj, 110); object_set_stride(obj, 7); // Pass 7 frames per tick jump_dir = 1; @@ -2283,7 +2309,7 @@ int har_act(object *obj, int act_type) { // at -100 frames (seems to be about right) object_set_playback_direction(obj, PLAY_BACKWARDS); object_set_tick_pos(obj, -110); - vx = (h->back_speed * direction * -1); + vx = (h->back_speedf * direction * -1); object_set_stride(obj, 7); // Pass 7 frames per tick jump_dir = -1; } else if(last_input == '8') { @@ -2295,11 +2321,12 @@ int har_act(object *obj, int act_type) { } if(input_staleness <= 6 && (h->inputs[1] == '1' || h->inputs[1] == '2' || h->inputs[1] == '3')) { // jumping from crouch makes you jump 25% higher - vy = h->superjump_speed; + vy = h->superjump_speedf; } object_set_vel(obj, vec2f_create(vx, vy)); har_event_jump(h, jump_dir, ctrl); break; + } } return 1; } @@ -2361,7 +2388,7 @@ void har_finished(object *obj) { } else if(h->state == STATE_RECOIL && h->health <= 0) { h->state = STATE_DEFEAT; har_set_ani(obj, h->custom_defeat_animation ? h->custom_defeat_animation : ANIM_DEFEAT, 0); - } else if((h->state == STATE_RECOIL || h->state == STATE_STANDING_UP) && h->endurance < 1.0f) { + } else if((h->state == STATE_RECOIL || h->state == STATE_STANDING_UP) && h->endurance < 1) { if(h->state == STATE_RECOIL) { har_event_recover(h, ctrl); } @@ -2495,8 +2522,8 @@ int har_create(object *obj, af *af_data, int dir, int har_id, int pilot_id, int // af_data->health); // The stun cap is calculated as follows // HAR Endurance * 3.6 * (Pilot Endurance + 16) / 23 - local->endurance_max = local->endurance = af_data->endurance * 3.6 * (pilot->endurance + 16) / 23; - log_debug("HAR endurance is %f with pilot endurance %d and base endurance %f", local->endurance, pilot->endurance, + local->endurance_max = local->endurance = af_data->endurance * 36 * (pilot->endurance + 16) / 23 / 10; + log_debug("HAR endurance is %d with pilot endurance %d and base endurance %d", local->endurance, pilot->endurance, af_data->endurance); // fwd speed = (Agility + 20) / 30 * fwd speed // back speed = (Agility + 20) / 30 * back speed @@ -2509,15 +2536,13 @@ int har_create(object *obj, af *af_data, int dir, int har_id, int pilot_id, int // Insanius: I went ahead and changed the formulas to use division instead of multiplication since it's more precise // for us Insanius: jump speed = speed up * vertical_agility_modifier * 212 / 256 Insanius: superjump speed = speed // up * vertical_agility_modifier * 266 / 256 - float horizontal_agility_modifier = ((float)gp->pilot->agility + 35) / 45; - float vertical_agility_modifier = ((float)gp->pilot->agility + 20) / 30; - obj->horizontal_velocity_modifier = horizontal_agility_modifier; - obj->vertical_velocity_modifier = vertical_agility_modifier; - local->jump_speed = (((float)gp->pilot->agility + 35) / 45) * af_data->jump_speed * 216 / 256; - local->superjump_speed = (((float)gp->pilot->agility + 35) / 45) * af_data->jump_speed * 266 / 256; - local->fall_speed = (((float)gp->pilot->agility + 20) / 30) * af_data->fall_speed; - local->fwd_speed = (((float)gp->pilot->agility + 20) / 30) * af_data->forward_speed; - local->back_speed = (((float)gp->pilot->agility + 20) / 30) * af_data->reverse_speed; + obj->horizontal_velocity_modifierf = fixedpt_fromint(gp->pilot->agility + 35) / 45; + obj->vertical_velocity_modifierf = fixedpt_fromint(gp->pilot->agility + 20) / 30; + local->jump_speed = af_data->jump_speed * (gp->pilot->agility + 35) / 45 * 216 / 256; + local->superjump_speedf = af_data->jump_speed * (gp->pilot->agility + 35) / 45 * 266 / 256; + local->fall_speed = af_data->fall_speed * (gp->pilot->agility + 20) / 30; + local->fwd_speedf = af_data->forward_speed * (gp->pilot->agility + 20) / 30; + local->back_speedf = af_data->reverse_speed * (gp->pilot->agility + 20) / 30; // TODO calculate a better value here local->stride = lrint(1 + (gp->pilot->agility / 20)); log_debug("setting HAR stride to %d", local->stride); @@ -2609,15 +2634,20 @@ int har_create(object *obj, af *af_data, int dir, int har_id, int pilot_id, int // fixup a bunch of stuff based on player stats + // ad-hoc fixed point + int const APPENDAGE_POWER_FIXP = 52; // chosen so that APPENDAGE_POWER_P_192 can be a round number + int const APPENDAGE_POWER_P_192 = 10; // 0.192 in appendage power fixp + int leg_power = 0; + int arm_power = 0; + bool is_tournament = false; - float leg_power = 0.0f; - float arm_power = 0.0f; // cheap way to check if we're in tournament mode if(pilot->photo != NULL) { is_tournament = true; // (Limb Power + 3) * .192 - leg_power = (pilot->leg_power + 3) * 0.192f; - arm_power = (pilot->arm_power + 3) * 0.192f; + // 0.192 = 1/52nd + leg_power = (pilot->leg_power + 3) * APPENDAGE_POWER_P_192; + arm_power = (pilot->arm_power + 3) * APPENDAGE_POWER_P_192; } af_move *move; @@ -2689,7 +2719,8 @@ int har_create(object *obj, af *af_data, int dir, int har_id, int pilot_id, int case 1: // arm speed and power if(move->damage) { - move->damage = (move->damage * (25 + pilot->power) / 35 + 1) * arm_power; + move->damage = + (move->damage * (25 + pilot->power) / 35 + 1) * arm_power / APPENDAGE_POWER_FIXP; } if(move->ani.extra_string_count > 0) { // sometimes there's not enough extra strings, so take the last available @@ -2701,7 +2732,8 @@ int har_create(object *obj, af *af_data, int dir, int har_id, int pilot_id, int case 2: // leg speed and power if(move->damage) { - move->damage = (move->damage * (25 + pilot->power) / 35 + 1) * leg_power; + move->damage = + (move->damage * (25 + pilot->power) / 35 + 1) * leg_power / APPENDAGE_POWER_FIXP; } if(move->ani.extra_string_count > 0) { // sometimes there's not enough extra strings, so take the last available @@ -2713,19 +2745,22 @@ int har_create(object *obj, af *af_data, int dir, int har_id, int pilot_id, int case 3: // apply arm power for damage if(move->damage) { - move->damage = (move->damage * (25 + pilot->power) / 35 + 1) * arm_power; + move->damage = + (move->damage * (25 + pilot->power) / 35 + 1) * arm_power / APPENDAGE_POWER_FIXP; } break; case 4: // apply leg power for damage if(move->damage) { - move->damage = (move->damage * (25 + pilot->power) / 35 + 1) * leg_power; + move->damage = + (move->damage * (25 + pilot->power) / 35 + 1) * leg_power / APPENDAGE_POWER_FIXP; } break; case 5: // apply leg and arm power for damage if(move->damage) { - move->damage = (move->damage * (25 + pilot->power) / 35 + 1) * arm_power * leg_power; + move->damage = (move->damage * (25 + pilot->power) / 35 + 1) * arm_power / + APPENDAGE_POWER_FIXP * leg_power / APPENDAGE_POWER_FIXP; } break; } diff --git a/src/game/objects/har.h b/src/game/objects/har.h index 4327dcd9c..6d3a1cbb3 100644 --- a/src/game/objects/har.h +++ b/src/game/objects/har.h @@ -132,23 +132,23 @@ typedef struct har_t { uint8_t damage_done; // Damage was done this animation uint8_t damage_received; // Damage was received this animation uint8_t air_attacked; - uint8_t is_wallhugging; // HAR is standing right next to a wall - uint8_t is_grabbed; // Is being moved by another object. Set by ex, ey tags - float last_damage_value; // Last damage value taken - float last_stun_value; // Last stun value taken + uint8_t is_wallhugging; // HAR is standing right next to a wall + uint8_t is_grabbed; // Is being moved by another object. Set by ex, ey tags + int16_t last_damage_value; // Last damage value taken + int32_t last_stun_value; // Last stun value taken. HAR_ENDURANCE fixed point. - float jump_speed; // Agility generated speed modifier for jumping - float superjump_speed; // Agility generated speed modifier for jumping - float fall_speed; // Agility generated speed modifier for falling - float fwd_speed; // Agility generated speed modifier for falling - float back_speed; // Agility generated speed modifier for falling + fixedpt jump_speed; // Agility generated speed modifier for jumping + fixedpt superjump_speedf; // Agility generated speed modifier for jumping + fixedpt fall_speed; // Agility generated speed modifier for falling + fixedpt fwd_speedf; // Agility generated speed modifier for falling + fixedpt back_speedf; // Agility generated speed modifier for falling int in_stasis_ticks; // Handle stasis activator int throw_duration; uint8_t stride; int16_t health_max, health; - float endurance_max, endurance; + int32_t endurance_max, endurance; char inputs[11]; uint32_t input_change_tick; // last tick the input direction changed uint8_t hard_close; diff --git a/src/game/objects/hazard.c b/src/game/objects/hazard.c index d00bf8882..873819c5e 100644 --- a/src/game/objects/hazard.c +++ b/src/game/objects/hazard.c @@ -6,9 +6,12 @@ #include #include +static fixedpt const dest_threshold = fixedpt_fromint(5); +static fixedpt const speed_cap = fixedpt_fromint(3); +static int const recip_accel_scale = 16; // higher values = less acceleration + int orb_almost_there(vec2f a, vec2f b) { - vec2f dir = vec2f_sub(a, b); - return (dir.x >= -2.0f && dir.x <= 2.0f && dir.y >= -2.0f && dir.y <= 2.0f); + return vec2f_distsqr(a, b) < fixedpt_xmul(dest_threshold, dest_threshold); } void hazard_tick(object *obj) { @@ -31,19 +34,12 @@ void hazard_tick(object *obj) { // XXX come up with a better equation to randomize the destination obj->orbit_pos = obj->pos; obj->orbit_pos_vary = vec2f_create(0, 0); - float mag; int limit = 10; do { - obj->orbit_dest = - vec2f_create(random_float(&obj->gs->rand) * 320.0f, random_float(&obj->gs->rand) * 200.0f); - obj->orbit_dest_dir = vec2f_sub(obj->orbit_dest, obj->orbit_pos); - mag = sqrtf(obj->orbit_dest_dir.x * obj->orbit_dest_dir.x + - obj->orbit_dest_dir.y * obj->orbit_dest_dir.y); + obj->orbit_dest = vec2f_create(random_fixedpt(&obj->gs->rand, fixedpt_fromint(320)), + random_fixedpt(&obj->gs->rand, fixedpt_fromint(200))); limit--; - } while(mag < 80.0f && limit > 0); - - obj->orbit_dest_dir.x /= mag; - obj->orbit_dest_dir.y /= mag; + } while(limit > 0 && vec2f_distsqr(obj->orbit_dest, obj->orbit_pos) < fixedpt_fromint(80 * 80)); } } } @@ -78,30 +74,14 @@ void hazard_spawn_cb(object *parent, int id, vec2i pos, vec2f vel, uint8_t mp_fl vec2f generate_destination(object *obj) { vec2f old = obj->orbit_dest; - vec2f new = - vec2f_create((random_float(&obj->gs->rand) * 280.0f) + 20.0f, (random_float(&obj->gs->rand) * 160.0f) + 20.0f); - while(vec2f_dist(old, new) < 100) { - new = vec2f_create((random_float(&obj->gs->rand) * 280.0f) + 20.0f, - (random_float(&obj->gs->rand) * 160.0f) + 20.0f); - } + vec2f new; + do { + new = vec2f_create(random_fixedpt(&obj->gs->rand, fixedpt_fromint(280)) + fixedpt_fromint(20), + random_fixedpt(&obj->gs->rand, fixedpt_fromint(160)) + fixedpt_fromint(20)); + } while(vec2f_distsqr(old, new) < fixedpt_fromint(100 * 100)); return new; } -void accelerate_orbit(object *obj) { - float x_dist = obj->pos.x - obj->orbit_dest.x; - float y_dist = obj->pos.y - obj->orbit_dest.y; - float bigger = max2(x_dist, y_dist); - if(fabsf(bigger) > 20) { - bigger *= -1.0f; - } - if(obj->vel.x < 1.0f) { - obj->vel.x += x_dist / (bigger * 10); - } - if(obj->vel.y < 1.0f) { - obj->vel.y += y_dist / (bigger * 10); - } -} - void hazard_move(object *obj) { if(obj->orbit) { /* @@ -121,53 +101,39 @@ void hazard_move(object *obj) { obj->vel.y = 0.0f; }*/ - if((dist(obj->pos.x, obj->orbit_pos.x) >= dist(obj->orbit_dest.x, obj->orbit_pos.x)) && - (dist(obj->pos.y, obj->orbit_pos.y) >= dist(obj->orbit_dest.y, obj->orbit_pos.y))) { - obj->orbit_pos.x = obj->pos.x; - obj->orbit_pos.y = obj->pos.y; + if((obj->orbit_pos.fx - obj->pos.fx >= obj->orbit_pos.fx - obj->orbit_dest.fx) && + (obj->orbit_pos.fy - obj->pos.fy >= obj->orbit_pos.fy - obj->orbit_dest.fy)) { + obj->orbit_pos.fx = obj->pos.fx; + obj->orbit_pos.fy = obj->pos.fy; obj->orbit_dest = generate_destination(obj); - log_debug("new position is %f, %f", obj->orbit_dest.x, obj->orbit_dest.y); + log_debug("new position is %f, %f", fixedpt_tofloat(obj->orbit_dest.fx), + fixedpt_tofloat(obj->orbit_dest.fy)); } // accelerate_orbit(obj); - float x_dist = obj->pos.x - obj->orbit_dest.x; - float y_dist = obj->pos.y - obj->orbit_dest.y; - float bigger = fabsf(y_dist); - if(fabsf(x_dist) > fabsf(y_dist)) { - bigger = x_dist; - } + fixedpt x_dist = obj->orbit_dest.fx - obj->pos.fx; + fixedpt y_dist = obj->orbit_dest.fy - obj->pos.fy; + fixedpt bigger = fixedpt_max2(fixedpt_abs(x_dist), fixedpt_abs(y_dist)); - if(obj->orbit_dest.x > obj->pos.x) { - if(obj->vel.x < 1.0f) { - log_debug("accel +%f", x_dist / (bigger * 10)); - obj->vel.x += x_dist / (bigger * 10); - } - } - if(obj->orbit_dest.x < obj->pos.x) { - if(obj->vel.x < 1.0f) { - log_debug("accel -%f", x_dist / (bigger * 10)); - obj->vel.x -= x_dist / (bigger * 10); - } - } - if(obj->orbit_dest.y > obj->pos.y) { - if(obj->vel.y < 1.0f) { - log_debug("accel +%f", y_dist / (bigger * 10)); - obj->vel.y += y_dist / (bigger * 10); - } - } - if(obj->orbit_dest.y < obj->pos.y) { - if(obj->vel.y < 1.0f) { - log_debug("accel -%f", y_dist / (bigger * 10)); - obj->vel.y -= y_dist / (bigger * 10); - } + vec2f accel = vec2f_create(fixedpt_xdiv(x_dist, bigger) / recip_accel_scale, + fixedpt_xdiv(y_dist, bigger) / recip_accel_scale); + + // log_debug("dist %f %f", fixedpt_tofloat(x_dist), fixedpt_tofloat(y_dist)); + // log_debug("accel %f %f", fixedpt_tofloat(accel.fx), fixedpt_tofloat(accel.fy)); + + obj->vel = vec2f_add(obj->vel, accel); + + fixedpt speed = vec2f_mag(obj->vel); + if(speed > speed_cap) { + obj->vel = vec2f_div(obj->vel, fixedpt_xdiv(speed, speed_cap)); } // Make this object orbit around the center of the arena /*obj->vel.x += 0.01f;*/ /*obj->vel.y += 0.01f;*/ - obj->pos.x += obj->vel.x; - obj->pos.y += obj->vel.y; + obj->pos.fx += obj->vel.fx; + obj->pos.fy += obj->vel.fy; // obj->pos.x = obj->orbit_pos.x+obj->orbit_pos_vary.x; // obj->pos.y = obj->orbit_pos.y+obj->orbit_pos_vary.y; // obj->orbit_pos.x += 2*obj->orbit_dest_dir.x; @@ -184,11 +150,11 @@ int hazard_create(object *obj, scene *scene) { object_set_move_cb(obj, hazard_move); object_set_dynamic_tick_cb(obj, hazard_tick); - obj->orbit_pos.x = obj->pos.x; - obj->orbit_pos.y = obj->pos.y; - obj->orbit_dest = - vec2f_create((random_float(&obj->gs->rand) * 280.0f) + 20.0f, (random_float(&obj->gs->rand) * 160.0f) + 20.0f); - log_debug("new position is %f, %f", obj->orbit_dest.x, obj->orbit_dest.y); + obj->orbit_pos.fx = obj->pos.fx; + obj->orbit_pos.fy = obj->pos.fy; + obj->orbit_dest = vec2f_create(random_fixedpt(&obj->gs->rand, fixedpt_fromint(280)) + fixedpt_fromint(20), + random_fixedpt(&obj->gs->rand, fixedpt_fromint(160)) + fixedpt_fromint(20)); + log_debug("new position is %f, %f", fixedpt_tofloat(obj->orbit_dest.fx), fixedpt_tofloat(obj->orbit_dest.fy)); return 0; } diff --git a/src/game/objects/projectile.c b/src/game/objects/projectile.c index 1148d15e9..7e1160255 100644 --- a/src/game/objects/projectile.c +++ b/src/game/objects/projectile.c @@ -6,7 +6,7 @@ #include "utils/log.h" #include -#define IS_ZERO(n) (n < 0.1 && n > -0.1) +#define IS_ZERO(n) (n < fixedpt_rconst(0.1) && n > fixedpt_rconst(-0.1)) typedef struct projectile_local_t { uint8_t player_id; @@ -49,48 +49,47 @@ void projectile_move(object *obj) { game_player *player = game_state_get_player(gs, projectile_get_owner(obj)); object *obj_har = game_state_find_object(gs, game_player_get_har_obj_id(player)); - obj->pos.x += obj->vel.x; - obj->vel.y += obj->gravity; - obj->pos.y += obj->vel.y; + obj->vel.fy += obj->gravity; + obj->pos = vec2f_add(obj->pos, obj->vel); - float dampen = 0.7f; +#define dampen 7 / 10 // If wall bounce flag is on, bounce the projectile on wall hit // Otherwise kill it. if(local->wall_bounce) { - if(obj->pos.x < ARENA_LEFT_WALL) { - obj->pos.x = ARENA_LEFT_WALL; - obj->vel.x = -obj->vel.x * dampen; + if(obj->pos.fx < ARENA_LEFT_WALLF) { + obj->pos.fx = ARENA_LEFT_WALLF; + obj->vel.fx = -obj->vel.fx * dampen; } - if(obj->pos.x > ARENA_RIGHT_WALL) { - obj->pos.x = ARENA_RIGHT_WALL; - obj->vel.x = -obj->vel.x * dampen; + if(obj->pos.fx > ARENA_RIGHT_WALLF) { + obj->pos.fx = ARENA_RIGHT_WALLF; + obj->vel.fx = -obj->vel.fx * dampen; } // if not invincible, not ignoring bounds checking and actually has an X velocity (the latter two help with // shadow grab) - } else if(!local->invincible && !player_frame_isset(obj, "bh") && !IS_ZERO(obj->vel.x)) { - if(obj->pos.x < ARENA_LEFT_WALL) { - obj->pos.x = ARENA_LEFT_WALL; + } else if(!local->invincible && !player_frame_isset(obj, "bh") && !IS_ZERO(obj->vel.fx)) { + if(obj->pos.fx < ARENA_LEFT_WALLF) { + obj->pos.fx = ARENA_LEFT_WALLF; obj->animation_state.finished = 1; projectile_finished(obj); } - if(obj->pos.x > ARENA_RIGHT_WALL) { - obj->pos.x = ARENA_RIGHT_WALL; + if(obj->pos.fx > ARENA_RIGHT_WALLF) { + obj->pos.fx = ARENA_RIGHT_WALLF; obj->animation_state.finished = 1; projectile_finished(obj); } } - if(obj->pos.y > ARENA_FLOOR && local->wall_bounce) { - obj->pos.y = ARENA_FLOOR; - obj->vel.y = -obj->vel.y * dampen; - obj->vel.x = obj->vel.x * dampen; - } else if(obj->pos.y > ARENA_FLOOR) { - obj->pos.y = ARENA_FLOOR; + if(obj->pos.fy > ARENA_FLOORF && local->wall_bounce) { + obj->pos.fy = ARENA_FLOORF; + obj->vel.fy = -obj->vel.fy * dampen; + obj->vel.fx = obj->vel.fx * dampen; + } else if(obj->pos.fy > ARENA_FLOORF) { + obj->pos.fy = ARENA_FLOORF; obj->animation_state.finished = 1; projectile_finished(obj); } - if(obj->pos.y >= (ARENA_FLOOR - 5) && IS_ZERO(obj->vel.x) && obj->vel.y < obj->gravity * 1.1 && - obj->vel.y > obj->gravity * -1.1 && local->ground_freeze) { + if(obj->pos.fy >= (ARENA_FLOORF - fixedpt_fromint(5)) && IS_ZERO(obj->vel.fx) && + obj->vel.fy < obj->gravity * 11 / 10 && obj->vel.fy > obj->gravity * -11 / 10 && local->ground_freeze) { object_disable_rewind_tag(obj, 1); } diff --git a/src/game/objects/scrap.c b/src/game/objects/scrap.c index b1b7b97c9..d127a6a5c 100644 --- a/src/game/objects/scrap.c +++ b/src/game/objects/scrap.c @@ -2,42 +2,43 @@ #include "game/objects/arena_constraints.h" #define SCRAP_KEEPALIVE 220 -#define IS_ZERO(n) (n < 0.1 && n > -0.1) +#define IS_ZERO(n) (n < fixedpt_rconst(0.1) && n > fixedpt_rconst(-0.1)) // TODO: This is kind of quick and dirty, think of something better. void scrap_move(object *obj) { vec2f vel = object_get_vel(obj); - vec2i pos = object_get_pos(obj); + vec2f pos = obj->pos; if(object_is_rewind_tag_disabled(obj) > 0) { return; } - pos.x += vel.x; - vel.y += obj->gravity; - pos.y += vel.y; + pos.fx += vel.fx; + vel.fy += obj->gravity; + pos.fy += vel.fy; - float dampen = 0.4f; +#define dampen 4 / 10 - if(pos.x < ARENA_LEFT_WALL) { - pos.x = ARENA_LEFT_WALL; - vel.x = -vel.x * dampen; + if(pos.fx < ARENA_LEFT_WALLF) { + pos.fx = ARENA_LEFT_WALLF; + vel.fx = -vel.fx * dampen; } - if(pos.x > ARENA_RIGHT_WALL) { - pos.x = ARENA_RIGHT_WALL; - vel.x = -vel.x * dampen; + if(pos.fx > ARENA_RIGHT_WALLF) { + pos.fx = ARENA_RIGHT_WALLF; + vel.fx = -vel.fx * dampen; } - if(pos.y > ARENA_FLOOR) { - pos.y = ARENA_FLOOR; - vel.y = -vel.y * dampen; - vel.x = vel.x * dampen + (rand_float() - 0.5f) * 3.0; + if(pos.fy > ARENA_FLOORF) { + pos.fy = ARENA_FLOORF; + vel.fy = -vel.fy * dampen; + vel.fx = vel.fx * dampen + (rand_float() - 0.5f) * 3.0; } - if(IS_ZERO(vel.x)) - vel.x = 0; - object_set_pos(obj, pos); + if(IS_ZERO(vel.fx)) + vel.fx = 0; + obj->pos = pos; object_set_vel(obj, vel); // If object is at rest, just halt animation - if(pos.y >= (ARENA_FLOOR - 5) && IS_ZERO(vel.x) && vel.y < obj->gravity * 1.1 && vel.y > obj->gravity * -1.1) { + if(pos.fy >= (ARENA_FLOORF - fixedpt_fromint(5)) && IS_ZERO(vel.fx) && vel.fy < obj->gravity * 11 / 10 && + vel.fy > obj->gravity * -11 / 10) { object_disable_rewind_tag(obj, 1); } } diff --git a/src/game/protos/intersect.c b/src/game/protos/intersect.c index 1c45b31c5..3918b2329 100644 --- a/src/game/protos/intersect.c +++ b/src/game/protos/intersect.c @@ -135,7 +135,7 @@ int intersect_sprite_hitpoint(object *obj, object *target, int level, vec2i *poi if(hitpoint < sfc->w * sfc->h && sfc->data[hitpoint] != sfc->transparent) { hcoords[found++] = vec2i_create(xcoord, ycoord); if(found >= level) { - vec2f sum = vec2f_create(0, 0); + vec2i sum = vec2i_create(0, 0); for(int k = 0; k < level; k++) { sum.x += hcoords[k].x; sum.y += hcoords[k].y; diff --git a/src/game/protos/object.c b/src/game/protos/object.c index 0748c6f53..8d083449f 100644 --- a/src/game/protos/object.c +++ b/src/game/protos/object.c @@ -32,7 +32,7 @@ void object_create(object *obj, game_state *gs, vec2i pos, vec2f vel) { // remember the place we were spawned, the x= and y= tags are relative to that obj->start = vec2i_to_f(pos); obj->vel = vel; - obj->horizontal_velocity_modifier = obj->vertical_velocity_modifier = 1.0f; + obj->horizontal_velocity_modifierf = obj->vertical_velocity_modifierf = FIXEDPT_ONE; obj->direction = OBJECT_FACE_RIGHT; obj->y_percent = 1.0; obj->x_percent = 1.0; @@ -40,7 +40,7 @@ void object_create(object *obj, game_state *gs, vec2i pos, vec2f vel) { // Physics obj->layers = OBJECT_DEFAULT_LAYER; obj->group = GROUP_UNKNOWN; - obj->gravity = 0.0f; + obj->gravity = 0; // Video effect stuff obj->animation_video_effects = 0; @@ -305,33 +305,33 @@ void object_del_frame_effects(object *obj, uint32_t effects) { void object_apply_controllable_velocity(object *obj, bool is_projectile, char input) { if(player_frame_isset(obj, "cx")) { - float cx = player_frame_get(obj, "cx") / 10.0; + fixedpt cx = fixedpt_fromint(player_frame_get(obj, "cx")) / 10; if(!is_projectile) { - cx *= obj->horizontal_velocity_modifier; + cx = fixedpt_xmul(cx, obj->horizontal_velocity_modifierf); } if(input == '4') { - obj->vel.x -= cx * object_get_direction(obj); + obj->vel.fx -= cx * object_get_direction(obj); } else if(input == '6') { - obj->vel.x += cx * object_get_direction(obj); + obj->vel.fx += cx * object_get_direction(obj); } else if(input == '3' || input == '9') { - obj->vel.x += cx * 0.7 * object_get_direction(obj); + obj->vel.fx += fixedpt_xmul(cx, fixedpt_rconst(0.7)) * object_get_direction(obj); } else if(input == '1' || input == '7') { - obj->vel.x -= cx * 0.7 * object_get_direction(obj); + obj->vel.fx -= fixedpt_xmul(cx, fixedpt_rconst(0.7)) * object_get_direction(obj); } // CY needs CX to be set if(player_frame_isset(obj, "cy")) { - float cy = player_frame_get(obj, "cy") / 10.0; + fixedpt cy = fixedpt_fromint(player_frame_get(obj, "cy")) / 10; if(!is_projectile) { - cy *= obj->vertical_velocity_modifier; + cy = fixedpt_xmul(cy, obj->vertical_velocity_modifierf); } if(input == '8') { - obj->vel.y -= cy * object_get_direction(obj); + obj->vel.fy -= cy * object_get_direction(obj); } else if(input == '2') { - obj->vel.y += cy * object_get_direction(obj); + obj->vel.fy += cy * object_get_direction(obj); } else if(input == '3' || input == '1') { - obj->vel.y += cy * 0.7 * object_get_direction(obj); + obj->vel.fy += fixedpt_xmul(cy, fixedpt_rconst(0.7)) * object_get_direction(obj); } else if(input == '7' || input == '9') { - obj->vel.y -= cy * 0.7 * object_get_direction(obj); + obj->vel.fy -= fixedpt_xmul(cy, fixedpt_rconst(0.7)) * object_get_direction(obj); } } } @@ -360,20 +360,20 @@ void object_render(object *obj) { // Set Y coord, take into account sprite flipping if(rstate->flipmode & FLIP_VERTICAL) { - y = obj->pos.y - cur_sprite->pos.y + rstate->o_correction.y - object_get_size(obj).y; + y = object_get_pos(obj).y - cur_sprite->pos.y + rstate->o_correction.y - object_get_size(obj).y; if(obj->cur_animation->id == ANIM_JUMPING) { y -= 100; } } else { - y = obj->pos.y + cur_sprite->pos.y + rstate->o_correction.y; + y = object_get_pos(obj).y + cur_sprite->pos.y + rstate->o_correction.y; } // Set X coord, take into account the HAR facing. if(object_get_direction(obj) == OBJECT_FACE_LEFT) { - x = obj->pos.x - cur_sprite->pos.x + rstate->o_correction.x - object_get_size(obj).x; + x = object_get_pos(obj).x - cur_sprite->pos.x + rstate->o_correction.x - object_get_size(obj).x; } else { - x = obj->pos.x + cur_sprite->pos.x + rstate->o_correction.x; + x = object_get_pos(obj).x + cur_sprite->pos.x + rstate->o_correction.x; } // Centrify if scaled @@ -451,9 +451,9 @@ void object_render_shadow(object *obj) { // Determine X int flip_mode = obj->sprite_state.flipmode; - int x = obj->pos.x + cur_sprite->pos.x + obj->sprite_state.o_correction.x; + int x = object_get_pos(obj).x + cur_sprite->pos.x + obj->sprite_state.o_correction.x; if(object_get_direction(obj) == OBJECT_FACE_LEFT) { - x = (obj->pos.x + obj->sprite_state.o_correction.x) - cur_sprite->pos.x - object_get_size(obj).x; + x = (object_get_pos(obj).x + obj->sprite_state.o_correction.x) - cur_sprite->pos.x - object_get_size(obj).x; flip_mode ^= FLIP_HORIZONTAL; } @@ -578,7 +578,10 @@ void object_set_animation(object *obj, animation *ani) { // Debug texts if(obj->cur_animation->id == -1) { - log_debug("Custom object set to (x,y) = (%f,%f).", obj->pos.x, obj->pos.y); + char bufx[FIXEDPT_STR_BUFSIZE], bufy[FIXEDPT_STR_BUFSIZE]; + fixedpt_str(obj->pos.fx, bufx, sizeof(bufx), -1); + fixedpt_str(obj->pos.fy, bufy, sizeof(bufy), -1); + log_debug("Custom object set to (x,y) = (%s,%s).", bufx, bufy); } else { /*log_debug("Animation object %d set to (x,y) = (%f,%f) with \"%s\".", */ /*obj->cur_animation->id,*/ @@ -673,11 +676,11 @@ void object_set_layers(object *obj, int layers) { void object_set_group(object *obj, int group) { obj->group = group; } -void object_set_gravity(object *obj, float gravity) { +void object_set_gravity(object *obj, fixedpt gravity) { obj->gravity = gravity; } -float object_get_gravity(const object *obj) { +fixedpt object_get_gravity(const object *obj) { return obj->gravity; } int object_get_group(const object *obj) { @@ -753,29 +756,32 @@ int object_px(const object *obj) { int object_py(const object *obj) { return vec2f_to_i(obj->pos).y; } -float object_vx(const object *obj) { - return obj->vel.x; +fixedpt object_vxf(const object *obj) { + return obj->vel.fx; } -float object_vy(const object *obj) { - return obj->vel.y; +fixedpt object_vyf(const object *obj) { + return obj->vel.fy; } void object_set_px(object *obj, int val) { - obj->pos.x = val; + obj->pos.fx = fixedpt_fromint(val); } void object_set_py(object *obj, int val) { - obj->pos.y = val; + obj->pos.fy = fixedpt_fromint(val); } -void object_set_vx(object *obj, float val) { - obj->vel.x = val; +void object_set_vxf(object *obj, fixedpt val) { + obj->vel.fx = val; } -void object_set_vy(object *obj, float val) { - obj->vel.y = val; +void object_set_vyf(object *obj, fixedpt val) { + obj->vel.fy = val; } vec2i object_get_pos(const object *obj) { return vec2f_to_i(obj->pos); } +vec2f object_get_fpos(const object *obj) { + return obj->pos; +} vec2f object_get_vel(const object *obj) { return obj->vel; } @@ -817,7 +823,7 @@ void object_set_destroy_cb(object *obj, object_state_del_cb cbf, void *userdata) } int object_is_airborne(const object *obj) { - return obj->pos.y < ARENA_FLOOR || obj->vel.y < 0; + return obj->pos.fy < fixedpt_fromint(ARENA_FLOOR) || obj->vel.fy < 0; } /* Attaches one object to another. Positions are synced to this from the attached. */ diff --git a/src/game/protos/object.h b/src/game/protos/object.h index 52219a3e4..610a5a714 100644 --- a/src/game/protos/object.h +++ b/src/game/protos/object.h @@ -79,8 +79,8 @@ struct object_t { vec2f start; vec2f pos; vec2f vel; - float vertical_velocity_modifier; - float horizontal_velocity_modifier; + fixedpt vertical_velocity_modifierf; + fixedpt horizontal_velocity_modifierf; int8_t direction; int8_t group; // Set when moving against the wall either by continuous velocity or by instantaneous position changes. @@ -94,7 +94,6 @@ struct object_t { int8_t orbit; float orbit_tick; vec2f orbit_dest; - vec2f orbit_dest_dir; vec2f orbit_pos; vec2f orbit_pos_vary; @@ -102,7 +101,7 @@ struct object_t { float x_percent; float y_percent; - float gravity; + fixedpt gravity; // Bitmask for several video effects (shadow, etc.) uint32_t frame_video_effects; //< Effects that only last for current frame @@ -202,7 +201,7 @@ void object_apply_controllable_velocity(object *obj, bool is_projectile, char in void object_set_layers(object *obj, int layers); void object_set_group(object *obj, int group); -void object_set_gravity(object *obj, float gravity); +void object_set_gravity(object *obj, fixedpt gravity); void object_set_userdata(object *obj, void *ptr); void *object_get_userdata(const object *obj); @@ -221,7 +220,7 @@ int object_get_repeat(const object *obj); void object_set_direction(object *obj, int dir); int object_get_direction(const object *obj); -float object_get_gravity(const object *obj); +fixedpt object_get_gravity(const object *obj); int object_get_group(const object *obj); int object_get_layers(const object *obj); @@ -244,6 +243,7 @@ int object_get_pal_limit(const object *obj); vec2i object_get_size(const object *obj); vec2i object_get_pos(const object *obj); +vec2f object_get_fpos(const object *obj); vec2f object_get_vel(const object *obj); void object_set_pos(object *obj, vec2i pos); @@ -253,8 +253,8 @@ int object_w(const object *obj); int object_h(const object *obj); int object_px(const object *obj); int object_py(const object *obj); -float object_vx(const object *obj); -float object_vy(const object *obj); +fixedpt object_vxf(const object *obj); +fixedpt object_vyf(const object *obj); void object_set_px(object *obj, int val); void object_set_py(object *obj, int val); diff --git a/src/game/protos/player.c b/src/game/protos/player.c index a3c2d07e3..b99736e33 100644 --- a/src/game/protos/player.c +++ b/src/game/protos/player.c @@ -146,16 +146,16 @@ void player_describe_frame(const sd_script_frame *frame) { void player_describe_object(object *obj) { log_debug("Object:"); - log_debug(" - Start: %d, %d", obj->start.x, obj->start.y); - log_debug(" - Position: %d, %d", obj->pos.x, obj->pos.y); - log_debug(" - Velocity: %d, %d", obj->vel.x, obj->vel.y); + log_debug(" - Start: %f, %f", fixedpt_tofloat(obj->start.fx), fixedpt_tofloat(obj->start.fy)); + log_debug(" - Position: %f, %f", fixedpt_tofloat(obj->pos.fx), fixedpt_tofloat(obj->pos.fy)); + log_debug(" - Velocity: %f, %f", fixedpt_tofloat(obj->vel.fx), fixedpt_tofloat(obj->vel.fy)); if(obj->cur_sprite_id) { sprite *cur_sprite = animation_get_sprite(obj->cur_animation, obj->cur_sprite_id); log_debug(" - Pos: %d, %d", cur_sprite->pos.x, cur_sprite->pos.y); log_debug(" - Size: %d, %d", cur_sprite->data->w, cur_sprite->data->h); player_sprite_state *rstate = &obj->sprite_state; - log_debug("CURRENT = %d - %d + %d - %d", obj->pos.y, cur_sprite->pos.y, rstate->o_correction.y, - cur_sprite->data->h); + log_debug("CURRENT = %f - %d + %d - %d", fixedpt_tofloat(obj->pos.fy), cur_sprite->pos.y, + rstate->o_correction.y, cur_sprite->data->h); } } @@ -249,7 +249,7 @@ void player_run(object *obj) { if(sd_script_isset(frame, "ac")) { // force the har to face the center of the arena - if(obj->pos.x > 160) { + if(obj->pos.fx > fixedpt_fromint(160)) { object_set_direction(obj, OBJECT_FACE_LEFT); } else { object_set_direction(obj, OBJECT_FACE_RIGHT); @@ -260,8 +260,8 @@ void player_run(object *obj) { int destination = 160; if(sd_script_isset(frame, "am") && sd_script_isset(frame, "e")) { // destination is the enemy's position - destination = enemy->pos.x - trans_x; - if(obj->pos.x > enemy->pos.x) { + destination = fixedpt_toint(enemy->pos.fx) - trans_x; + if(obj->pos.fx > enemy->pos.fx) { object_set_direction(obj, OBJECT_FACE_LEFT); } else { object_set_direction(obj, OBJECT_FACE_RIGHT); @@ -283,8 +283,9 @@ void player_run(object *obj) { } // clear this trans_x = 0; - if(sd_script_get(frame, "bm") == 10 && destination > 0 && fabsf(obj->pos.x - destination) > 5.0) { - log_debug("HAR walk to %d from %d", destination, obj->pos.x); + if(sd_script_get(frame, "bm") == 10 && destination > 0 && + fixedpt_abs(obj->pos.fx - fixedpt_fromint(destination)) > fixedpt_fromint(5)) { + log_debug("HAR walk to %d from %f", destination, fixedpt_tofloat(obj->pos.fx)); har_walk_to(obj, destination); return; } @@ -292,21 +293,22 @@ void player_run(object *obj) { if(sd_script_isset(frame, "h")) { // Hover, reset all velocities to 0 on every frame - obj->vel.x = 0; - obj->vel.y = 0; + obj->vel.fx = 0; + obj->vel.fy = 0; } } if(sd_script_isset(frame, "e") && enemy) { - log_debug("my position %f, %f, their position %f %f", obj->pos.x, obj->pos.y, enemy->pos.x, enemy->pos.y); + log_debug("my position %f, %f, their position %f %f", fixedpt_tofloat(obj->pos.fx), + fixedpt_tofloat(obj->pos.fy), fixedpt_tofloat(enemy->pos.fx), fixedpt_tofloat(enemy->pos.fy)); // Set speed to 0, since we're being controlled by animation tag system - obj->vel.x = 0; - obj->vel.y = 0; + obj->vel.fx = 0; + obj->vel.fy = 0; // Reset position to enemy coordinates and make sure facing is set correctly - obj->pos.x = enemy->pos.x; - obj->pos.y = enemy->pos.y; + obj->pos.fx = enemy->pos.fx; + obj->pos.fy = enemy->pos.fy; object_set_direction(obj, object_get_direction(enemy) * -1); // log_debug("E: pos.x = %f, pos.y = %f", obj->pos.x, obj->pos.y); } @@ -315,19 +317,20 @@ void player_run(object *obj) { // Set to ground if(sd_script_isset(frame, "g")) { - obj->vel.y = 0; - obj->pos.y = ARENA_FLOOR; + obj->vel.fy = 0; + obj->pos.fy = ARENA_FLOORF; } if(sd_script_isset(frame, "at") && enemy) { - log_debug("my position %f, %f, their position %f %f", obj->pos.x, obj->pos.y, enemy->pos.x, enemy->pos.y); + log_debug("my position %f, %f, their position %f %f", fixedpt_tofloat(obj->pos.fx), + fixedpt_tofloat(obj->pos.fy), fixedpt_tofloat(enemy->pos.fx), fixedpt_tofloat(enemy->pos.fy)); // set the object's X position to be behind the opponent - if(obj->pos.x > enemy->pos.x) { // From right to left - obj->pos.x = enemy->pos.x - object_get_size(obj).x / 2; + if(obj->pos.fx > enemy->pos.fx) { // From right to left + obj->pos.fx = enemy->pos.fx - fixedpt_fromint(object_get_size(obj).x / 2); } else { // From left to right - obj->pos.x = enemy->pos.x + object_get_size(enemy).x / 2; + obj->pos.fx = enemy->pos.fx + fixedpt_fromint(object_get_size(enemy).x / 2); } object_set_direction(obj, object_get_direction(obj) * -1); } @@ -335,28 +338,28 @@ void player_run(object *obj) { // Handle vx+/-, vy+/-, x+/-. y+/- if(trans_x || trans_y) { if(sd_script_isset(frame, "v")) { - obj->vel.x = (trans_x * (mp & 0x20 ? -1 : 1)) * obj->horizontal_velocity_modifier; - obj->vel.y = trans_y * obj->horizontal_velocity_modifier; + obj->vel.fx = (trans_x * (mp & 0x20 ? -1 : 1)) * obj->horizontal_velocity_modifierf; + obj->vel.fy = trans_y * obj->horizontal_velocity_modifierf; // log_debug("vel x+%d, y+%d to x=%f, y=%f", trans_x * (mp & 0x20 ? -1 : 1), trans_y, obj->vel.x, // obj->vel.y); } else { - obj->pos.x += trans_x * (mp & 0x20 ? -1 : 1); + obj->pos.fx += fixedpt_fromint(trans_x * (mp & 0x20 ? -1 : 1)); if(!ab_flag) { - if(obj->pos.x < ARENA_LEFT_WALL && obj->group == GROUP_HAR) { + if(obj->pos.fx < ARENA_LEFT_WALLF && obj->group == GROUP_HAR) { if(sd_script_isset(frame, "e") && enemy) { - enemy->pos.x += ARENA_LEFT_WALL - obj->pos.x; + enemy->pos.fx += ARENA_LEFT_WALLF - obj->pos.fx; } - obj->pos.x = ARENA_LEFT_WALL; + obj->pos.fx = ARENA_LEFT_WALLF; obj->wall_collision = true; - } else if(obj->pos.x > ARENA_RIGHT_WALL && obj->group == GROUP_HAR) { + } else if(obj->pos.fx > ARENA_RIGHT_WALLF && obj->group == GROUP_HAR) { if(sd_script_isset(frame, "e") && enemy) { - enemy->pos.x -= obj->pos.x - ARENA_RIGHT_WALL; + enemy->pos.fx -= obj->pos.fx - ARENA_RIGHT_WALLF; } - obj->pos.x = ARENA_RIGHT_WALL; + obj->pos.fx = ARENA_RIGHT_WALLF; obj->wall_collision = true; } } - obj->pos.y += trans_y; + obj->pos.fy += fixedpt_fromint(trans_y); // log_debug("pos x+%d, y+%d to x=%f, y=%f", trans_x * (mp & 0x20 ? -1 : 1), trans_y, obj->pos.x, // obj->pos.y); } @@ -364,23 +367,24 @@ void player_run(object *obj) { // Handle slide operations on self if(obj->slide_state.timer > 0) { - obj->pos.x += obj->slide_state.vel.x; - obj->pos.y += obj->slide_state.vel.y; + obj->pos.fx += obj->slide_state.vel.fx; + obj->pos.fy += obj->slide_state.vel.fy; obj->slide_state.timer--; } // Handle slide in relation to enemy if(obj->enemy_slide_state.timer > 0 && enemy) { - log_debug("my position %f, %f, their position %f %f", obj->pos.x, obj->pos.y, enemy->pos.x, enemy->pos.y); + log_debug("my position %f, %f, their position %f %f", fixedpt_tofloat(obj->pos.fx), + fixedpt_tofloat(obj->pos.fy), fixedpt_tofloat(enemy->pos.fx), fixedpt_tofloat(enemy->pos.fy)); obj->enemy_slide_state.duration++; - obj->pos.x = enemy->pos.x + obj->enemy_slide_state.dest.x; - obj->pos.y = enemy->pos.y + obj->enemy_slide_state.dest.y; + obj->pos.fx = enemy->pos.fx + fixedpt_fromint(obj->enemy_slide_state.dest.x); + obj->pos.fy = enemy->pos.fy + fixedpt_fromint(obj->enemy_slide_state.dest.y); obj->enemy_slide_state.timer--; } if(obj->group == GROUP_HAR && enemy && !ab_flag) { - obj->pos.x = clampf(obj->pos.x, ARENA_LEFT_WALL, ARENA_RIGHT_WALL); + obj->pos.fx = fixedpt_clamp(obj->pos.fx, ARENA_LEFT_WALLF, ARENA_RIGHT_WALLF); } // If frame changed, do something @@ -389,15 +393,15 @@ void player_run(object *obj) { if(sd_script_isset(frame, "m") && state->spawn != NULL) { int mx = 0; int my = 0; - float vx = 0; - float vy = 0; + fixedpt vx = 0; + fixedpt vy = 0; if(obj->animation_state.shadow_corner_hack && sd_script_get(frame, "m") == 65 && enemy) { - log_debug("my position %f, %f, their position %f %f", obj->pos.x, obj->pos.y, enemy->pos.x, - enemy->pos.y); - mx = enemy->pos.x; - my = enemy->pos.y; + log_debug("my position %f, %f, their position %f %f", fixedpt_tofloat(obj->pos.fx), + fixedpt_tofloat(obj->pos.fy), fixedpt_tofloat(enemy->pos.fx), fixedpt_tofloat(enemy->pos.fy)); + mx = fixedpt_toint(enemy->pos.fx); + my = fixedpt_toint(enemy->pos.fy); } // Staring X coordinate for new animation @@ -407,7 +411,7 @@ void player_run(object *obj) { mx = random_int(&obj->gs->rand, 320 - 2 * mm) + mrx; log_debug("randomized mx as %d", mx); } else if(sd_script_isset(frame, "mx")) { - mx = obj->start.x + (sd_script_get(frame, "mx") * object_get_direction(obj)); + mx = fixedpt_toint(obj->start.fx) + (sd_script_get(frame, "mx") * object_get_direction(obj)); } // Staring Y coordinate for new animation @@ -417,15 +421,15 @@ void player_run(object *obj) { my = random_int(&obj->gs->rand, 320 - 2 * mm) + mry; log_debug("randomized my as %d", my); } else if(sd_script_isset(frame, "my")) { - my = obj->start.y + sd_script_get(frame, "my"); + my = fixedpt_toint(obj->start.fy) + sd_script_get(frame, "my"); } // Angle/speed for new animation if(sd_script_isset(frame, "ma")) { int ma = sd_script_get(frame, "ma"); - vx = cosf(ma); - vy = sinf(ma); - log_debug("MA is set! angle = %d, vx = %f, vy = %f", ma, vx, vy); + vx = fixedpt_cos(ma * FIXEDPT_PI / 180); + vy = fixedpt_sin(ma * FIXEDPT_PI / 180); + log_debug("MA is set! angle = %d, vx = %f, vy = %f", ma, fixedpt_tofloat(vx), fixedpt_tofloat(vy)); } // Special positioning for certain desert arena sprites @@ -478,7 +482,7 @@ void player_run(object *obj) { if(sd_script_isset(frame, "sb")) { panning = clamp(sd_script_get(frame, "sb"), -100, 100) / 100.0f; } else { - panning = (obj->pos.x - 160) / 160.0f; + panning = (fixedpt_toint(obj->pos.fx) - 160) / 160.0f; } if(obj->sound_translation_table) { int sound_id = obj->sound_translation_table[sd_script_get(frame, "s")] - 1; @@ -560,7 +564,8 @@ void player_run(object *obj) { // If UA is set, force other HAR to damage animation if(sd_script_isset(frame, "ua") && enemy && enemy->cur_animation->id != 9) { - log_debug("my position %f, %f, their position %f %f", obj->pos.x, obj->pos.y, enemy->pos.x, enemy->pos.y); + log_debug("my position %f, %f, their position %f %f", fixedpt_tofloat(obj->pos.fx), + fixedpt_tofloat(obj->pos.fy), fixedpt_tofloat(enemy->pos.fx), fixedpt_tofloat(enemy->pos.fy)); har_set_ani(enemy, 9, 0); } @@ -573,11 +578,11 @@ void player_run(object *obj) { return; } - if(sd_script_isset(frame, "bu") && obj->vel.y < 0.0f) { - float x_dist = dist(obj->pos.x, 160); + if(sd_script_isset(frame, "bu") && obj->vel.fy < 0) { + fixedpt x_dist = fixedpt_fromint(160) - obj->pos.fx; // assume that bu is used in conjunction with 'vy-X' and that we want to land in the center of the arena - obj->slide_state.vel.x = x_dist / (obj->vel.y * -2); - obj->slide_state.timer = obj->vel.y * -2; + obj->slide_state.vel.fx = fixedpt_xdiv(x_dist, (obj->vel.fy * -2)); + obj->slide_state.timer = fixedpt_toint(obj->vel.fy * -2); } // handle scaling on the Y axis @@ -590,7 +595,7 @@ void player_run(object *obj) { obj->slide_state.vel = vec2f_create(0, 0); } if(sd_script_isset(frame, "x=")) { - obj->pos.x = obj->start.x + (sd_script_get(frame, "x=") * object_get_direction(obj)); + obj->pos.fx = obj->start.fx + fixedpt_fromint(sd_script_get(frame, "x=") * object_get_direction(obj)); // Find frame ID by tick int frame_id = sd_script_next_frame_with_tag(&state->parser, "x=", state->current_tick); @@ -600,9 +605,9 @@ void player_run(object *obj) { int mr = sd_script_get_tick_pos_at_frame(&state->parser, frame_id); int r = mr - state->current_tick - frame->tick_len; int next_x = sd_script_get(sd_script_get_frame(&state->parser, frame_id), "x="); - int slide = obj->start.x + (next_x * object_get_direction(obj)); - if(slide != obj->pos.x) { - obj->slide_state.vel.x = dist(obj->pos.x, slide) / (float)(frame->tick_len + r); + fixedpt slide = obj->start.fx + fixedpt_fromint(next_x * object_get_direction(obj)); + if(slide != obj->pos.fx) { + obj->slide_state.vel.fx = (slide - obj->pos.fx) / (frame->tick_len + r); obj->slide_state.timer = frame->tick_len + r; /* log_debug("Slide object %d for X = %f for a total of %d + %d = %d ticks.", obj->cur_animation->id, @@ -614,7 +619,7 @@ void player_run(object *obj) { } } if(sd_script_isset(frame, "y=")) { - obj->pos.y = obj->start.y + sd_script_get(frame, "y="); + obj->pos.fy = obj->start.fy + fixedpt_fromint(sd_script_get(frame, "y=")); // Find frame ID by tick int frame_id = sd_script_next_frame_with_tag(&state->parser, "y=", state->current_tick); @@ -624,9 +629,9 @@ void player_run(object *obj) { int mr = sd_script_get_tick_pos_at_frame(&state->parser, frame_id); int r = mr - state->current_tick - frame->tick_len; int next_y = sd_script_get(sd_script_get_frame(&state->parser, frame_id), "y="); - int slide = next_y + obj->start.y; - if(slide != obj->pos.y) { - obj->slide_state.vel.y = dist(obj->pos.y, slide) / (float)(frame->tick_len + r); + fixedpt slide = fixedpt_fromint(next_y) + obj->start.fy; + if(slide != obj->pos.fy) { + obj->slide_state.vel.fy = (slide - obj->pos.fy) / (frame->tick_len + r); obj->slide_state.timer = frame->tick_len + r; /* log_debug("Slide object %d for Y = %f for a total of %d + %d = %d ticks.", obj->cur_animation->id, diff --git a/src/game/scenes/arena.c b/src/game/scenes/arena.c index cd9363d65..1545c6714 100644 --- a/src/game/scenes/arena.c +++ b/src/game/scenes/arena.c @@ -555,19 +555,20 @@ void arena_har_hit_wall_hook(int player_id, int wall, scene *scene) { } // HAR must be in the air to be get faceplanted to a wall. - if(o_har->pos.y >= ARENA_FLOOR - 10) { + if(o_har->pos.fy >= ARENA_FLOORF - fixedpt_fromint(10)) { // TODO: Grounded desert wall logic return; } - float abs_velocity_h = fabsf(o_har->vel.x) / o_har->horizontal_velocity_modifier; + fixedpt abs_velocity_h = fixedpt_xdiv(fixedpt_abs(o_har->vel.fx), o_har->horizontal_velocity_modifierf); - if(abs_velocity_h > 2) { - int tolerance = arena_get_wall_slam_tolerance(scene->gs); + if(abs_velocity_h > fixedpt_fromint(2)) { + fixedpt tolerance = fixedpt_fromint(arena_get_wall_slam_tolerance(scene->gs)); // log_debug("Checking if %f velocity will wallslam", abs_velocity_h); - if((abs_velocity_h + 0.5f) > tolerance && (h->state == STATE_FALLEN || h->state == STATE_RECOIL)) { + if((abs_velocity_h + fixedpt_rconst(0.5)) > tolerance && + (h->state == STATE_FALLEN || h->state == STATE_RECOIL)) { h->state = STATE_WALLDAMAGE; bk_info *info = bk_get_info(scene->bk_data, 20 + wall); @@ -587,7 +588,7 @@ void arena_har_hit_wall_hook(int player_id, int wall, scene *scene) { info = bk_get_info(scene->bk_data, 22); if(info) { // Only Power Plant has the electric overlay effect object *obj2 = omf_calloc(1, sizeof(object)); - object_create(obj2, scene->gs, vec2i_create(o_har->pos.x, o_har->pos.y), vec2f_create(0, 0)); + object_create(obj2, scene->gs, vec2f_to_i(o_har->pos), vec2f_create(0, 0)); object_set_stl(obj2, scene->bk_data->sound_translation_table); object_set_animation(obj2, &info->ani); object_attach_to(obj2, o_har); @@ -603,8 +604,8 @@ void arena_har_hit_wall_hook(int player_id, int wall, scene *scene) { int variance = rand_int(20) - 10; int anim_no = rand_int(2) + 24; // log_debug("XXX anim = %d, variance = %d", anim_no, variance); - int pos_y = o_har->pos.y - object_get_size(o_har).y + variance + i * 25; - vec2i coord = vec2i_create(o_har->pos.x, pos_y); + vec2i coord = vec2f_to_i(o_har->pos); + coord.y -= object_get_size(o_har).y + variance + i * 25; object *dust = omf_calloc(1, sizeof(object)); object_create(dust, scene->gs, coord, vec2f_create(0, 0)); object_set_stl(dust, scene->bk_data->sound_translation_table); @@ -613,22 +614,22 @@ void arena_har_hit_wall_hook(int player_id, int wall, scene *scene) { } // Wallhit sound - float d = ((float)o_har->pos.x) / 640.0f; + float d = fixedpt_tofloat(o_har->pos.fx) / 640.0f; float pos_pan = d - 0.25f; game_state_play_sound(o_har->gs, 68, 1.0f, pos_pan, 2.0f); // Set hit animation object_set_animation(o_har, &af_get_move(h->af_data, ANIM_DAMAGE)->ani); object_set_repeat(o_har, 0); - scene->gs->screen_shake_horizontal = 3 * fabsf(o_har->vel.x); + scene->gs->screen_shake_horizontal = fixedpt_toint(3 * fixedpt_abs(o_har->vel.fx)); // from MASTER.DAT object_set_custom_string(o_har, "hQ1-hQ7-x-3Q5-x-2L5-x-2M900"); if(wall == 1) { - o_har->pos.x = ARENA_RIGHT_WALL - 2; + o_har->pos.fx = ARENA_RIGHT_WALLF - fixedpt_fromint(2); object_set_direction(o_har, OBJECT_FACE_RIGHT); } else { - o_har->pos.x = ARENA_LEFT_WALL + 2; + o_har->pos.fx = ARENA_LEFT_WALLF + fixedpt_fromint(2); object_set_direction(o_har, OBJECT_FACE_LEFT); } } else { @@ -833,6 +834,7 @@ uint32_t arena_state_hash(game_state *gs) { har *har = obj_har->userdata; vec2i pos = object_get_pos(obj_har); vec2f vel = object_get_vel(obj_har); + // TODO: Hash full-precision fixedpt pos, vel uint32_t x = (uint32_t)pos.x; uint32_t y = (uint32_t)pos.y; uint32_t health = (uint32_t)har->health; @@ -845,7 +847,7 @@ uint32_t arena_state_hash(game_state *gs) { hash = ((hash << 5) + hash) + y; hash = ((hash << 5) + hash) + health; hash = ((hash << 5) + hash) + endurance; - hash = ((hash << 5) + hash) + (uint32_t)vel.x; + hash = ((hash << 5) + hash) + fixedpt_toint(vel.fx); // we are inconsistent on applying gravity // hash = ((hash << 5) + hash) + (uint32_t)vel.y; hash = ((hash << 5) + hash) + har->state; @@ -908,8 +910,8 @@ void arena_state_dump(game_state *gs, char *buf, size_t bufsize) { "player %d power %d agility %d endurance %d HAR id %d pos %d,%d, health %d, endurance %f, " "velocity %f,%f, state %s, executing_move %d cur_anim %d\n", i, player->pilot->power, player->pilot->agility, player->pilot->endurance, har->id, pos.x, pos.y, - har->health, (float)har->endurance, vel.x, vel.y, state_name(har->state), har->executing_move, - obj_har->cur_animation->id); + har->health, (float)har->endurance, fixedpt_tofloat(vel.fx), fixedpt_tofloat(vel.fy), + state_name(har->state), har->executing_move, obj_har->cur_animation->id); } } @@ -1102,7 +1104,7 @@ bool har_in_defeat_animation(object *obj) { } bool defeated_at_rest(object *obj) { - return har_in_defeat_animation(obj) && !object_is_airborne(obj) && obj->vel.x == 0.0f; + return har_in_defeat_animation(obj) && !object_is_airborne(obj) && obj->vel.fx == 0; } bool har_unfinished_victory(object *obj) { @@ -1224,9 +1226,10 @@ void arena_dynamic_tick(scene *scene, int paused) { // Create the object object *scrap = omf_calloc(1, sizeof(object)); int anim_no = rand_int(3) + ANIM_SCRAP_METAL; - object_create(scrap, gs, pos, vec2f_create(velx, vely)); + object_create(scrap, gs, pos, + vec2f_create(fixedpt_rconst(velx), fixedpt_rconst(vely))); // abusing rconst object_set_animation(scrap, &af_get_move(h->af_data, anim_no)->ani); - object_set_gravity(scrap, 0.4f); + object_set_gravity(scrap, fixedpt_rconst(0.4)); object_set_pal_offset(scrap, object_get_pal_offset(h_obj)); object_set_pal_limit(scrap, object_get_pal_limit(h_obj)); object_set_layers(scrap, LAYER_SCRAP); @@ -1241,9 +1244,9 @@ void arena_dynamic_tick(scene *scene, int paused) { // check some invariants assert(player_frame_isset(obj_har[0], "ab") || - (obj_har[0]->pos.x >= ARENA_LEFT_WALL && obj_har[0]->pos.x <= ARENA_RIGHT_WALL)); + (obj_har[0]->pos.fx >= ARENA_LEFT_WALLF && obj_har[0]->pos.fx <= ARENA_RIGHT_WALLF)); assert(player_frame_isset(obj_har[1], "ab") || - (obj_har[1]->pos.x >= ARENA_LEFT_WALL && obj_har[1]->pos.x <= ARENA_RIGHT_WALL)); + (obj_har[1]->pos.fx >= ARENA_LEFT_WALLF && obj_har[1]->pos.fx <= ARENA_RIGHT_WALLF)); if(hars[0]->health == 0) { assert(hars[0]->state == STATE_DEFEAT || hars[0]->state == STATE_RECOIL || hars[0]->state == STATE_FALLEN || hars[0]->state == STATE_NONE || hars[0]->state == STATE_WALLDAMAGE); @@ -1426,7 +1429,7 @@ static void arena_debug(scene *scene) { text_render_mode(&tconf_debug, TEXT_DEFAULT, 230 - (strlen(buf) * fnt->w), 5, 250, 6, buf); } - snprintf(buf, sizeof(buf), "%.2f", hars[i]->endurance); + snprintf(buf, sizeof(buf), "%d", hars[i]->endurance); if(i == 0) { text_render_mode(&tconf_debug, TEXT_DEFAULT, 70, 12, 250, 6, buf); } else { @@ -1440,7 +1443,8 @@ static void arena_debug(scene *scene) { text_render_mode(&tconf_debug, TEXT_DEFAULT, 315 - (strlen(buf) * fnt->w), 48, 250, 6, buf); } - snprintf(buf, sizeof(buf), "pos: %.3f %.3f", obj_har[i]->pos.x, obj_har[i]->pos.y); + snprintf(buf, sizeof(buf), "pos: %.3f %.3f", fixedpt_tofloat(obj_har[i]->pos.fx), + fixedpt_tofloat(obj_har[i]->pos.fy)); if(i == 0) { text_render_mode(&tconf_debug, TEXT_DEFAULT, 5, 56, 250, 6, buf); @@ -1448,7 +1452,8 @@ static void arena_debug(scene *scene) { text_render_mode(&tconf_debug, TEXT_DEFAULT, 315 - (strlen(buf) * fnt->w), 56, 250, 6, buf); } - snprintf(buf, sizeof(buf), "vel: %.3f %.3f", obj_har[i]->vel.x, obj_har[i]->vel.y); + snprintf(buf, sizeof(buf), "vel: %.3f %.3f", fixedpt_tofloat(obj_har[i]->vel.fx), + fixedpt_tofloat(obj_har[i]->vel.fy)); if(i == 0) { text_render_mode(&tconf_debug, TEXT_DEFAULT, 5, 62, 250, 6, buf); @@ -1496,8 +1501,7 @@ void arena_clone(scene *src, scene *dst) { int arena_get_wall_slam_tolerance(game_state *gs) { if(gs->match_settings.hazards) { - int arena_id = gs->this_id - SCENE_ARENA0; - if(arena_id == 2) { + if(gs->this_id == SCENE_ARENA2) { return wall_slam_tolerance_powerplant; } } diff --git a/src/resources/af.c b/src/resources/af.c index e016df085..c7655036d 100644 --- a/src/resources/af.c +++ b/src/resources/af.c @@ -10,10 +10,10 @@ void af_create(af *a, void *src) { a->id = sdaf->file_id; a->endurance = sdaf->endurance; a->health = sdaf->health; - a->forward_speed = sdaf->forward_speed; - a->reverse_speed = sdaf->reverse_speed; - a->jump_speed = sdaf->jump_speed; - a->fall_speed = sdaf->fall_speed; + a->forward_speed = fixedpt_fromint(sdaf->forward_speed) / 256; + a->reverse_speed = fixedpt_fromint(sdaf->reverse_speed) / 256; + a->jump_speed = fixedpt_fromint(sdaf->jump_speed) / 256; + a->fall_speed = fixedpt_fromint(sdaf->fall_speed) / 256; // Sound translation table memcpy(a->sound_translation_table, sdaf->soundtable, 30); diff --git a/src/resources/af.h b/src/resources/af.h index e2e452596..6282e2ab2 100644 --- a/src/resources/af.h +++ b/src/resources/af.h @@ -9,10 +9,10 @@ typedef struct af_t { unsigned int id; float endurance; unsigned int health; - float forward_speed; - float reverse_speed; - float jump_speed; - float fall_speed; + fixedpt forward_speed; + fixedpt reverse_speed; + fixedpt jump_speed; + fixedpt fall_speed; array sprites; array moves; char sound_translation_table[30]; diff --git a/src/resources/af_move.c b/src/resources/af_move.c index 4a7ab697c..fca5c434d 100644 --- a/src/resources/af_move.c +++ b/src/resources/af_move.c @@ -11,7 +11,7 @@ void af_move_create(af_move *move, array *sprites, void *src, int id) { move->category = sdmv->category; move->damage = sdmv->damage_amount; move->raw_damage = sdmv->damage_amount; - move->stun = 0.0f; + move->stun = 0; move->points = sdmv->points * 400; move->block_damage = sdmv->block_damage; move->block_stun = sdmv->block_stun; diff --git a/src/resources/af_move.h b/src/resources/af_move.h index cc66ce017..5780ba8bb 100644 --- a/src/resources/af_move.h +++ b/src/resources/af_move.h @@ -17,9 +17,9 @@ typedef struct af_move_t { uint8_t block_stun; uint8_t throw_duration; uint8_t extra_string_selector; - float damage; - float raw_damage; // used to store the raw damage, unmodified by pilot stats - float stun; + int16_t damage; + uint8_t raw_damage; // used to store the raw damage, unmodified by pilot stats + int32_t stun; str move_string; str footer_string; #ifdef DEBUGMODE diff --git a/src/utils/fixedptc.h b/src/utils/fixedptc.h new file mode 100644 index 000000000..d159b9475 --- /dev/null +++ b/src/utils/fixedptc.h @@ -0,0 +1,459 @@ +#ifndef _FIXEDPTC_H_ +#define _FIXEDPTC_H_ + +/* + * fixedptc.h is a 32-bit or 64-bit fixed point numeric library. + * + * 16-bit support has also been added. + * + * The symbol FIXEDPT_BITS, if defined before this library header file + * is included, determines the number of bits in the data type (its "width"). + * The default width is 32-bit (FIXEDPT_BITS=32) and it can be used + * on any recent C99 compiler. The 64-bit precision (FIXEDPT_BITS=64) is + * available on compilers which implement 128-bit "long long" types. This + * precision has been tested on GCC 4.2+. + * + * The FIXEDPT_WBITS symbols governs how many bits are dedicated to the + * "whole" part of the number (to the left of the decimal point). The larger + * this width is, the larger the numbers which can be stored in the fixedpt + * number. The rest of the bits (available in the FIXEDPT_FBITS symbol) are + * dedicated to the fraction part of the number (to the right of the decimal + * point). + * + * Since the number of bits in both cases is relatively low, many complex + * functions (more complex than div & mul) take a large hit on the precision + * of the end result because errors in precision accumulate. + * This loss of precision can be lessened by increasing the number of + * bits dedicated to the fraction part, but at the loss of range. + * + * Adventurous users might utilize this library to build two data types: + * one which has the range, and one which has the precision, and carefully + * convert between them (including adding two number of each type to produce + * a simulated type with a larger range and precision). + * + * The ideas and algorithms have been cherry-picked from a large number + * of previous implementations available on the Internet. + * Tim Hartrick has contributed cleanup and 64-bit support patches. + * + * == Special notes for the 32-bit precision == + * Signed 32-bit fixed point numeric library for the 24.8 format. + * The specific limits are -8388608.999... to 8388607.999... and the + * most precise number is 0.00390625. In practice, you should not count + * on working with numbers larger than a million or to the precision + * of more than 2 decimal places. Make peace with the fact that PI + * is 3.14 here. :) + */ + +/*- + * Copyright (c) 2010-2012 Ivan Voras + * Copyright (c) 2012 Tim Hartrick + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef FIXEDPT_BITS +#define FIXEDPT_BITS 32 +#endif + +#include + +#if FIXEDPT_BITS == 16 +typedef int16_t fixedpt; +typedef int32_t fixedptd; +typedef uint16_t fixedptu; +typedef uint32_t fixedptud; +#define FIXEDPT_MIN INT16_MIN +#define FIXEDPT_MAX INT16_MAX +#define FIXEDPTU_MAX UINT16_MAX +#define FIXEDPT_WCHARS 5 // 16 bit numbers can use at most 5 decimal digits +#elif FIXEDPT_BITS == 32 +typedef int32_t fixedpt; +typedef int64_t fixedptd; +typedef uint32_t fixedptu; +typedef uint64_t fixedptud; +#define FIXEDPT_MIN INT32_MIN +#define FIXEDPT_MAX INT32_MAX +#define FIXEDPTU_MAX UINT32_MAX +#define FIXEDPT_WCHARS 10 // 32 bit numbers can use at most 10 decimal digits +#elif FIXEDPT_BITS == 64 +typedef int64_t fixedpt; +typedef __int128_t fixedptd; +typedef uint64_t fixedptu; +typedef __uint128_t fixedptud; +#define FIXEDPT_MIN INT64_MIN +#define FIXEDPT_MAX INT64_MAX +#define FIXEDPTU_MAX UINT64_MAX // 64 bit numbers can use at most 20 decimal digits +#define FIXEDPT_WCHARS 20 +#else +#error "FIXEDPT_BITS must be equal to 16, 32, or 64" +#endif + +#ifndef FIXEDPT_WBITS +#define FIXEDPT_WBITS 24 +#endif + +#if FIXEDPT_WBITS >= FIXEDPT_BITS +#error "FIXEDPT_WBITS must be less than or equal to FIXEDPT_BITS" +#endif + +#define FIXEDPT_VCSID "$Id$" + +#define FIXEDPT_FBITS (FIXEDPT_BITS - FIXEDPT_WBITS) +#define FIXEDPT_FMASK (((fixedpt)1 << FIXEDPT_FBITS) - 1) + +#define fixedpt_rconst(R) ((fixedpt)((R) * FIXEDPT_ONE + ((R) >= 0 ? 0.5 : -0.5))) +#define fixedpt_fromint(I) ((fixedptd)(I) * FIXEDPT_ONE) +#define fixedpt_toint(F) ((F) / FIXEDPT_ONE) +#define fixedpt_add(A, B) ((A) + (B)) +#define fixedpt_sub(A, B) ((A) - (B)) +#define fixedpt_xmul(A, B) ((fixedpt)(((fixedptd)(A) * (fixedptd)(B)) / FIXEDPT_ONE)) +#define fixedpt_xmul3(A, B, C) fixedpt_xmul(fixedpt_xmul((A), (B)), (C)) +#define fixedpt_xdiv(A, B) ((fixedpt)(((fixedptd)(A) * FIXEDPT_ONE) / (fixedptd)(B))) +#define fixedpt_fracpart(A) ((fixedptud)(A) & FIXEDPT_FMASK) + +#define FIXEDPT_ONE ((fixedpt)((fixedpt)1 << FIXEDPT_FBITS)) +#define FIXEDPT_ONE_HALF (FIXEDPT_ONE >> 1) +#define FIXEDPT_TWO (FIXEDPT_ONE + FIXEDPT_ONE) +#define FIXEDPT_PI fixedpt_rconst(3.14159265358979323846) +#define FIXEDPT_TWO_PI fixedpt_rconst(2 * 3.14159265358979323846) +#define FIXEDPT_HALF_PI fixedpt_rconst(3.14159265358979323846 / 2) +#define FIXEDPT_E fixedpt_rconst(2.7182818284590452354) + +/* fixedpt is meant to be usable in environments without floating point support + * (e.g. microcontrollers, kernels), so we can't use floating point types directly. + * Putting them only in macros will effectively make them optional. */ +#define fixedpt_tofloat(T) ((float)((T) * ((float)(1) / (float)(1L << FIXEDPT_FBITS)))) + +#if FIXEDPT_MAX <= INT_MAX +#define fixedpt_abs(A) abs((A)) +#elif FIXEDPT_MAX <= INT64_MAX +#define fixedpt_abs(A) llabs((A)) +#else +#define fixedpt_abs(A) ((A) < 0 ? -(A) : (A)) +#endif + +static inline fixedpt fixedpt_clamp(fixedpt val, fixedpt min, fixedpt max) { + if(val > max) + return max; + if(val < min) + return min; + return val; +} + +static inline fixedpt fixedpt_max2(fixedpt a, fixedpt b) { + return a > b ? a : b; +} + +static inline fixedpt fixedpt_min2(fixedpt a, fixedpt b) { + return a < b ? a : b; +} + +/* Multiplies two fixedpt numbers, returns the result. */ +static inline fixedpt fixedpt_mul(fixedpt A, fixedpt B) { + return (((fixedptd)A * (fixedptd)B) >> FIXEDPT_FBITS); +} + +/* Divides two fixedpt numbers, returns the result. */ +static inline fixedpt fixedpt_div(fixedpt A, fixedpt B) { + return (((fixedptd)A << FIXEDPT_FBITS) / (fixedptd)B); +} + +/* + * Note: adding and substracting fixedpt numbers can be done by using + * the regular integer operators + and -. + * + * Second note: multiplying and dividing a fixedpt number by an integer + * can be done with the regular integer operators * and /, if you're a + * bad enough programmer (https://youtu.be/EmZvrEPWx6w?t=18)-- dividing + * an integer by a fixed point divisor *does* require using fixedpt_xdiv. + */ + +// TODO: calculate FIXEDPT_STR_BUFSIZE? or just keep it large enough for fixedpt_str to not blow up. +#define FIXEDPT_STR_BUFSIZE 32 + +/** + * Convert the given fixedpt number to a decimal string. + * The max_dec argument specifies how many decimal digits to the right + * of the decimal point to generate. If set to -1, the "default" number + * of decimal digits will be used (2 for 32-bit fixedpt width, 10 for + * 64-bit fixedpt width); If set to -2, "all" of the digits will + * be returned, meaning there will be invalid, bogus digits outside the + * specified precisions. If set to -3, all the zero digits will be trimmed + * from the end of the decimal digits. + * + * Returns the number of bytes written to `str`, not counting the null terminator. + */ +static inline size_t fixedpt_str(fixedpt A, char *str, size_t bufsize, int max_dec) { + // boy this function sure is safe with pointers :( + size_t nint = 0; // number of integer digits + size_t nfrac = 0; // number of fractional digits + size_t slen = 0; // string length used + + // accumulator for integer digits, can hold the max number of digits for FIXEDPT_BITS + char acc[FIXEDPT_WCHARS] = {0}; + fixedptud fr, ip; + fixedptud magnitude = (fixedptud)(fixedptd)A; + const fixedptud one = (fixedptud)1 << FIXEDPT_BITS; + const fixedptud mask = one - 1; + size_t num_places = 0; + + // return if the destination is null or too small + if(str == NULL || bufsize <= 0) { + return 0; + } else if(bufsize < 2) { + str[0] = '\0'; + return 0; + } + + if(max_dec == -1) { +#if FIXEDPT_BITS == 16 || FIXEDPT_BITS == 32 +#if FIXEDPT_FBITS < 16 + num_places = 2; +#else + num_places = 4; +#endif +#elif FIXEDPT_BITS == 64 + num_places = 10; +#else +#error Invalid width +#endif + } else if(max_dec == -2) { + num_places = 15; + } else if(max_dec == -3) { + num_places = 15; + } else if(max_dec < 0) { + // other negative values of max_dec not allowed + return 0; + } else { + num_places = max_dec; + } + + if(A < 0) { + str[slen++] = '-'; + magnitude *= -1; + } + + // Accumulate the integer digits in reverse order + // by dividing the integer component by 10 each time. + // Use do/while so at least a 0 is written + ip = fixedpt_toint(magnitude); + do { + acc[nint++] = '0' + ip % 10; + ip /= 10; + } while(ip != 0 && nint < sizeof(acc) - 1); + + // reverse the accumulated digits into the output string + while(nint > 0 && slen < bufsize - 1) { + str[slen++] = acc[--nint]; + } + + // check if we want, and have space for, decimal digits + if(num_places > 0 && slen < bufsize - 1) { + str[slen++] = '.'; + + fr = (fixedpt_fracpart(magnitude) << FIXEDPT_WBITS) & mask; + while(nfrac < num_places && slen < bufsize - 1) { + fr = (fr & mask) * 10; + + str[slen++] = '0' + (fr >> FIXEDPT_BITS) % 10; + nfrac++; + } + + // trim any trailing 0s, if desired + while(max_dec == -3 && nfrac > 0 && str[slen - 1] == '0') { + slen--; + nfrac--; + } + + // if we didn't add any decimal values, omit the decimal place + if(nfrac == 0) { + slen--; + } + } + + size_t bytes_used = slen < bufsize ? slen : bufsize - 1; + str[bytes_used] = '\0'; + return bytes_used; +} + +/* Converts the given fixedpt number into a string, using a static + * (non-threadsafe) string buffer */ +static inline char *fixedpt_cstr(const fixedpt A, const int max_dec) { + static char str[25]; + + fixedpt_str(A, str, sizeof(str), max_dec); + return (str); +} + +/* Returns the square root of the given number, or -1 in case of error */ +static inline fixedpt fixedpt_sqrt(fixedpt A) { + int invert = 0; + int iter = FIXEDPT_FBITS; + int l, i; + + if(A < 0) + return (-1); + if(A == 0 || A == FIXEDPT_ONE) + return (A); + if(A < FIXEDPT_ONE && A > 6) { + invert = 1; + A = fixedpt_div(FIXEDPT_ONE, A); + } + if(A > FIXEDPT_ONE) { + int s = A; + + iter = 0; + while(s > 0) { + s >>= 2; + iter++; + } + } + + /* Newton's iterations */ + l = (A >> 1) + 1; + for(i = 0; i < iter; i++) + l = (l + fixedpt_div(A, l)) >> 1; + if(invert) + return (fixedpt_div(FIXEDPT_ONE, l)); + return (l); +} + +/* Returns the sine of the given fixedpt number. + * Note: the loss of precision is extraordinary! */ +static inline fixedpt fixedpt_sin(fixedpt fp) { + int sign = 1; + fixedpt sqr, result; + const fixedpt SK[2] = {fixedpt_rconst(7.61e-03), fixedpt_rconst(1.6605e-01)}; + + fp %= 2 * FIXEDPT_PI; + if(fp < 0) + fp = FIXEDPT_PI * 2 + fp; + if((fp > FIXEDPT_HALF_PI) && (fp <= FIXEDPT_PI)) + fp = FIXEDPT_PI - fp; + else if((fp > FIXEDPT_PI) && (fp <= (FIXEDPT_PI + FIXEDPT_HALF_PI))) { + fp = fp - FIXEDPT_PI; + sign = -1; + } else if(fp > (FIXEDPT_PI + FIXEDPT_HALF_PI)) { + fp = (FIXEDPT_PI << 1) - fp; + sign = -1; + } + sqr = fixedpt_mul(fp, fp); + result = SK[0]; + result = fixedpt_mul(result, sqr); + result -= SK[1]; + result = fixedpt_mul(result, sqr); + result += FIXEDPT_ONE; + result = fixedpt_mul(result, fp); + return sign * result; +} + +/* Returns the cosine of the given fixedpt number */ +static inline fixedpt fixedpt_cos(fixedpt A) { + return (fixedpt_sin(FIXEDPT_HALF_PI - A)); +} + +/* Returns the tangens of the given fixedpt number */ +static inline fixedpt fixedpt_tan(fixedpt A) { + return fixedpt_div(fixedpt_sin(A), fixedpt_cos(A)); +} + +/* Returns the value exp(x), i.e. e^x of the given fixedpt number. */ +static inline fixedpt fixedpt_exp(fixedpt fp) { + fixedpt xabs, k, z, R, xp; + const fixedpt LN2 = fixedpt_rconst(0.69314718055994530942); + const fixedpt LN2_INV = fixedpt_rconst(1.4426950408889634074); + const fixedpt EXP_P[5] = { + fixedpt_rconst(1.66666666666666019037e-01), fixedpt_rconst(-2.77777777770155933842e-03), + fixedpt_rconst(6.61375632143793436117e-05), fixedpt_rconst(-1.65339022054652515390e-06), + fixedpt_rconst(4.13813679705723846039e-08), + }; + + if(fp == 0) + return (FIXEDPT_ONE); + xabs = fixedpt_abs(fp); + k = fixedpt_mul(xabs, LN2_INV); + k += FIXEDPT_ONE_HALF; + k &= ~FIXEDPT_FMASK; + if(fp < 0) + k = -k; + fp -= fixedpt_mul(k, LN2); + z = fixedpt_mul(fp, fp); + /* Taylor */ + R = FIXEDPT_TWO + + fixedpt_mul( + z, EXP_P[0] + + fixedpt_mul(z, EXP_P[1] + + fixedpt_mul(z, EXP_P[2] + fixedpt_mul(z, EXP_P[3] + fixedpt_mul(z, EXP_P[4]))))); + xp = FIXEDPT_ONE + fixedpt_div(fixedpt_mul(fp, FIXEDPT_TWO), R - fp); + if(k < 0) + k = FIXEDPT_ONE >> (-k >> FIXEDPT_FBITS); + else + k = FIXEDPT_ONE << (k >> FIXEDPT_FBITS); + return (fixedpt_mul(k, xp)); +} + +/* Returns the natural logarithm of the given fixedpt number. */ +static inline fixedpt fixedpt_ln(fixedpt x) { + fixedpt log2, xi; + fixedpt f, s, z, w, R; + const fixedpt LN2 = fixedpt_rconst(0.69314718055994530942); + const fixedpt LG[7] = {fixedpt_rconst(6.666666666666735130e-01), fixedpt_rconst(3.999999999940941908e-01), + fixedpt_rconst(2.857142874366239149e-01), fixedpt_rconst(2.222219843214978396e-01), + fixedpt_rconst(1.818357216161805012e-01), fixedpt_rconst(1.531383769920937332e-01), + fixedpt_rconst(1.479819860511658591e-01)}; + + if(x < 0) + return (0); + if(x == 0) { + return FIXEDPT_MAX; + } + + log2 = 0; + xi = x; + while(xi > FIXEDPT_TWO) { + xi >>= 1; + log2++; + } + f = xi - FIXEDPT_ONE; + s = fixedpt_div(f, FIXEDPT_TWO + f); + z = fixedpt_mul(s, s); + w = fixedpt_mul(z, z); + R = fixedpt_mul(w, LG[1] + fixedpt_mul(w, LG[3] + fixedpt_mul(w, LG[5]))) + + fixedpt_mul(z, LG[0] + fixedpt_mul(w, LG[2] + fixedpt_mul(w, LG[4] + fixedpt_mul(w, LG[6])))); + return (fixedpt_mul(LN2, (log2 << FIXEDPT_FBITS)) + f - fixedpt_mul(s, f - R)); +} + +/* Returns the logarithm of the given base of the given fixedpt number */ +static inline fixedpt fixedpt_log(fixedpt x, fixedpt base) { + return (fixedpt_div(fixedpt_ln(x), fixedpt_ln(base))); +} + +/* Return the power value (n^exp) of the given fixedpt numbers */ +static inline fixedpt fixedpt_pow(fixedpt n, fixedpt exp) { + if(exp == 0) + return (FIXEDPT_ONE); + if(n < 0) + return 0; + return (fixedpt_exp(fixedpt_mul(fixedpt_ln(n), exp))); +} + +#endif diff --git a/src/utils/miscmath.h b/src/utils/miscmath.h index b4fbbb231..eddc11e02 100644 --- a/src/utils/miscmath.h +++ b/src/utils/miscmath.h @@ -59,10 +59,6 @@ static inline unsigned udist(unsigned a, unsigned b) { return (a > b) ? a - b : b - a; } -static inline float dist(float a, float b) { - return fabsf((a < b ? a : b) - (a > b ? a : b)) * (a < b ? 1.0f : -1.0f); -} - static inline size_t smin2(size_t a, size_t b) { return (a > b) ? b : a; } diff --git a/src/utils/random.c b/src/utils/random.c index a1a4e3a09..d169cb544 100644 --- a/src/utils/random.c +++ b/src/utils/random.c @@ -1,4 +1,5 @@ #include "utils/random.h" +#include #include // A simple psuedorandom number generator @@ -26,6 +27,11 @@ float random_float(struct random_t *r) { return (float)random_intmax(r) / (float)UINT_MAX; } +fixedpt random_fixedpt(struct random_t *r, fixedpt max) { + static_assert(sizeof(fixedpt) <= sizeof(int), "or else rework this"); + return random_int(r, max + 1); +} + void rand_seed(uint32_t seed) { random_seed(&rand_state, seed); } @@ -41,3 +47,6 @@ uint32_t rand_intmax(void) { float rand_float(void) { return random_float(&rand_state); } +fixedpt rand_fixedpt(fixedpt max) { + return random_fixedpt(&rand_state, max); +} diff --git a/src/utils/random.h b/src/utils/random.h index 1410e9782..1e18b7ac3 100644 --- a/src/utils/random.h +++ b/src/utils/random.h @@ -1,6 +1,7 @@ #ifndef RANDOM_H #define RANDOM_H +#include "fixedptc.h" #include /* Note: do not typedef random here because it will clash with stdlib's (linux?) random() */ @@ -21,6 +22,9 @@ uint32_t random_intmax(struct random_t *r); /* Return a random float in 0 <= r <= 1.0f */ float random_float(struct random_t *r); +/* Return a random fixedpt in 0 <= r <= max */ +fixedpt random_fixedpt(struct random_t *r, fixedpt max); + /* Same as the above but keeps an internal state * Use as a replacement for rand() */ @@ -29,5 +33,6 @@ uint32_t rand_get_seed(void); uint32_t rand_int(uint32_t upperbound); uint32_t rand_intmax(void); float rand_float(void); +fixedpt rand_fixedpt(fixedpt max); #endif // RANDOM_H diff --git a/src/utils/vec.h b/src/utils/vec.h index f522f9e75..4212bcc37 100644 --- a/src/utils/vec.h +++ b/src/utils/vec.h @@ -1,11 +1,12 @@ #ifndef VEC_H #define VEC_H +#include "fixedptc.h" #include typedef struct vec2f { - float x; - float y; + fixedpt fx; + fixedpt fy; } vec2f; typedef struct vec2i { @@ -32,51 +33,67 @@ static inline vec2i vec2i_mult(vec2i a, vec2i b) { } static inline vec2f vec2f_add(vec2f a, vec2f b) { - a.x += b.x; - a.y += b.y; + a.fx += b.fx; + a.fy += b.fy; return a; } static inline vec2f vec2f_sub(vec2f a, vec2f b) { - a.x -= b.x; - a.y -= b.y; + a.fx -= b.fx; + a.fy -= b.fy; return a; } static inline vec2f vec2f_mult(vec2f a, vec2f b) { - a.x *= b.x; - a.y *= b.y; + a.fx = fixedpt_xmul(a.fx, b.fx); + a.fy = fixedpt_xmul(a.fy, b.fy); + return a; +} + +static inline vec2f vec2f_div(vec2f a, fixedpt div) { + a.fx = fixedpt_xdiv(a.fx, div); + a.fy = fixedpt_xdiv(a.fy, div); return a; } static inline vec2i vec2f_to_i(vec2f f) { vec2i i; - i.x = f.x; - i.y = f.y; + i.x = fixedpt_toint(f.fx); + i.y = fixedpt_toint(f.fy); return i; } static inline vec2f vec2i_to_f(vec2i i) { vec2f f; - f.x = i.x; - f.y = i.y; + f.fx = fixedpt_fromint(i.x); + f.fy = fixedpt_fromint(i.y); return f; } -static inline float vec2f_mag(vec2f a) { - return sqrtf(a.x * a.x + a.y * a.y); +static inline fixedpt vec2f_magsqr(vec2f a) { + return fixedpt_xmul(a.fx, a.fx) + fixedpt_xmul(a.fy, a.fy); +} + +static inline fixedpt vec2f_mag(vec2f a) { + return fixedpt_sqrt(fixedpt_xmul(a.fx, a.fx) + fixedpt_xmul(a.fy, a.fy)); } static inline vec2f vec2f_norm(vec2f a) { - float mag = vec2f_mag(a); - a.x /= mag; - a.y /= mag; + fixedpt mag = vec2f_mag(a); + a.fx = fixedpt_xdiv(a.fx, mag); + a.fy = fixedpt_xdiv(a.fy, mag); return a; } -static inline float vec2f_dist(vec2f a, vec2f b) { +static inline fixedpt vec2f_distsqr(vec2f a, vec2f b) { + return vec2f_magsqr(vec2f_sub(a, b)); +} +static inline fixedpt vec2f_dist(vec2f a, vec2f b) { return vec2f_mag(vec2f_sub(a, b)); } +static inline fixedpt vec2f_manhattan_dist(vec2f a, vec2f b) { + return fixedpt_abs(a.fx - b.fx) + fixedpt_abs(a.fy - b.fy); +} static inline vec2i vec2i_create(int x, int y) { vec2i v; @@ -85,10 +102,10 @@ static inline vec2i vec2i_create(int x, int y) { return v; } -static inline vec2f vec2f_create(float x, float y) { +static inline vec2f vec2f_create(fixedpt x, fixedpt y) { vec2f v; - v.x = x; - v.y = y; + v.fx = x; + v.fy = y; return v; } diff --git a/testing/test_fixedptc_str.c b/testing/test_fixedptc_str.c new file mode 100644 index 000000000..fc92600fd --- /dev/null +++ b/testing/test_fixedptc_str.c @@ -0,0 +1,152 @@ +#include "utils/fixedptc.h" +#include + +void test_zero_value(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt zero = 0; + size_t written; + + // Default precision (-1) + written = fixedpt_str(zero, buf, sizeof(buf), -1); + CU_ASSERT_STRING_EQUAL(buf, "0.00"); + CU_ASSERT_EQUAL(written, 4); + + // Trim all decimals (-3) + written = fixedpt_str(zero, buf, sizeof(buf), -3); + CU_ASSERT_STRING_EQUAL(buf, "0"); + CU_ASSERT_EQUAL(written, 1); +} + +void test_positive_integers(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = 123 << FIXEDPT_FBITS; // 123.0 + size_t written; + + // With 2 decimal places + written = fixedpt_str(val, buf, sizeof(buf), 2); + CU_ASSERT_STRING_EQUAL(buf, "123.00"); + CU_ASSERT_EQUAL(written, 6); + + // Trim trailing zeros + written = fixedpt_str(val, buf, sizeof(buf), -3); + CU_ASSERT_STRING_EQUAL(buf, "123"); + CU_ASSERT_EQUAL(written, 3); +} + +void test_negative_numbers(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = -(456 << FIXEDPT_FBITS); // -456.0 + size_t written; + + written = fixedpt_str(val, buf, sizeof(buf), 2); + CU_ASSERT_STRING_EQUAL(buf, "-456.00"); + CU_ASSERT_EQUAL(written, 7); +} + +void test_exact_fractions(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = FIXEDPT_ONE + FIXEDPT_ONE_HALF; // 1.5 + size_t written; + + // Normal precision + written = fixedpt_str(val, buf, sizeof(buf), 1); + CU_ASSERT_STRING_EQUAL(buf, "1.5"); + CU_ASSERT_EQUAL(written, 3); + + // Trim zeros + written = fixedpt_str(val, buf, sizeof(buf), -3); + CU_ASSERT_STRING_EQUAL(buf, "1.5"); + CU_ASSERT_EQUAL(written, 3); +} + +void test_buffer_limits(void) { + char small_buf[5]; + fixedpt val = (123 << FIXEDPT_FBITS) + FIXEDPT_ONE_HALF; // 123.5 + size_t written; + + // Try to write to tiny buffer + written = fixedpt_str(val, small_buf, sizeof(small_buf), 2); + CU_ASSERT_STRING_EQUAL(small_buf, "123"); + CU_ASSERT_EQUAL(written, 3); +} + +void test_zero_trimming(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = (123 << FIXEDPT_FBITS) + (FIXEDPT_ONE / 4); // 123.25 + size_t written; + + // Add extra zeros + written = fixedpt_str(val, buf, sizeof(buf), 4); + CU_ASSERT_STRING_EQUAL(buf, "123.2500"); + CU_ASSERT_EQUAL(written, 8); + + // Trim to significant digits + written = fixedpt_str(val, buf, sizeof(buf), -3); + CU_ASSERT_STRING_EQUAL(buf, "123.25"); + CU_ASSERT_EQUAL(written, 6); +} + +void test_max_precision(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = FIXEDPT_ONE / 10; // 0.1 + size_t written; + + // Request maximum precision + written = fixedpt_str(val, buf, sizeof(buf), -2); + CU_ASSERT_STRING_EQUAL(buf, "0.097656250000000"); + CU_ASSERT_EQUAL(written, 17); +} + +void test_min_value(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = FIXEDPT_MIN; // INT32_MIN >> 8 + size_t written; + + written = fixedpt_str(val, buf, sizeof(buf), 2); + CU_ASSERT_STRING_EQUAL(buf, "-8388608.00"); + CU_ASSERT_EQUAL(written, 11); +} + +void test_near_min_value(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = FIXEDPT_MIN + (FIXEDPT_ONE / 4); + size_t written; + + written = fixedpt_str(val, buf, sizeof(buf), 2); + CU_ASSERT_STRING_EQUAL(buf, "-8388607.75"); + CU_ASSERT_EQUAL(written, 11); +} + +void test_max_value(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = FIXEDPT_MAX; // INT32_MAX >> 8 + size_t written; + + written = fixedpt_str(val, buf, sizeof(buf), 2); + CU_ASSERT_STRING_EQUAL(buf, "8388607.99"); + CU_ASSERT_EQUAL(written, 10); +} + +void test_near_max_value(void) { + char buf[FIXEDPT_STR_BUFSIZE]; + fixedpt val = FIXEDPT_MAX - (FIXEDPT_ONE / 4); + size_t written; + + written = fixedpt_str(val, buf, sizeof(buf), 2); + CU_ASSERT_STRING_EQUAL(buf, "8388607.74"); + CU_ASSERT_EQUAL(written, 10); +} + +void fixedpt_str_test_suite(CU_pSuite suite) { + CU_add_test(suite, "Zero Handling", test_zero_value); + CU_add_test(suite, "Positive Integers", test_positive_integers); + CU_add_test(suite, "Negative Numbers", test_negative_numbers); + CU_add_test(suite, "Exact Fractions", test_exact_fractions); + CU_add_test(suite, "Buffer Limits", test_buffer_limits); + CU_add_test(suite, "Zero Trimming", test_zero_trimming); + CU_add_test(suite, "Max Precision", test_max_precision); + CU_add_test(suite, "Min Value", test_min_value); + CU_add_test(suite, "Near Min Value", test_near_min_value); + CU_add_test(suite, "Max Value", test_max_value); + CU_add_test(suite, "Near Max Value", test_near_max_value); +} diff --git a/testing/test_main.c b/testing/test_main.c index 982a33771..173307959 100644 --- a/testing/test_main.c +++ b/testing/test_main.c @@ -19,6 +19,7 @@ void video_common_test_suite(CU_pSuite suite); int text_markup_suite_init(void); int text_markup_suite_free(void); void cp437_test_suite(CU_pSuite suite); +void fixedpt_str_test_suite(CU_pSuite suite); int main(int argc, char **argv) { CU_pSuite suite = NULL; @@ -109,6 +110,11 @@ int main(int argc, char **argv) { goto end; script_test_suite(suite); + suite = CU_add_suite("Fixed Point String Printing", NULL, NULL); + if(suite == NULL) + goto end; + fixedpt_str_test_suite(suite); + // Run tests CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); diff --git a/tools/aftool/main.c b/tools/aftool/main.c index 8ea47ea82..6437e2cf6 100644 --- a/tools/aftool/main.c +++ b/tools/aftool/main.c @@ -522,7 +522,7 @@ void af_get_key(sd_af_file *af, const char **key, int kcount) { printf("%d\n", af->exec_window); break; case 2: - printf("%f\n", af->endurance); + printf("%d\n", af->endurance); break; case 3: printf("%d\n", af->unknown_b); @@ -531,16 +531,16 @@ void af_get_key(sd_af_file *af, const char **key, int kcount) { printf("%d\n", af->health); break; case 5: - printf("%f\n", af->forward_speed); + printf("%d\n", af->forward_speed); break; case 6: - printf("%f\n", af->reverse_speed); + printf("%d\n", af->reverse_speed); break; case 7: - printf("%f\n", af->jump_speed); + printf("%d\n", af->jump_speed); break; case 8: - printf("%f\n", af->fall_speed); + printf("%d\n", af->fall_speed); break; case 9: printf("%d\n", af->unknown_c); @@ -638,13 +638,13 @@ void af_info(sd_af_file *af) { printf("AF File information:\n"); printf(" * File ID: %d\n", af->file_id); printf(" * Exec window: %d\n", af->exec_window); - printf(" * Endurance: %f\n", af->endurance); + printf(" * Endurance: %d\n", af->endurance); printf(" * Unknown B: %d\n", af->unknown_b); printf(" * Health: %d\n", af->health); - printf(" * Fwd speed: %f\n", af->forward_speed); - printf(" * Rev speed: %f\n", af->reverse_speed); - printf(" * Jump speed: %f\n", af->jump_speed); - printf(" * Fall speed: %f\n", af->fall_speed); + printf(" * Fwd speed: %d\n", af->forward_speed); + printf(" * Rev speed: %d\n", af->reverse_speed); + printf(" * Jump speed: %d\n", af->jump_speed); + printf(" * Fall speed: %d\n", af->fall_speed); printf(" * Unknown C: %d\n", af->unknown_c); printf(" * Animations: ");