diff --git a/CMakeLists.txt b/CMakeLists.txt index b099757..8e47a1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,18 +40,10 @@ set(ASSETS assets-cg/hud_wave2.png assets-cg/skillicons.png # Player animations - assets-cg/player/player_idle_up.png - assets-cg/player/player_idle_right.png - assets-cg/player/player_idle_down.png - assets-cg/player/player_idle_left.png - assets-cg/player/player_attack_up.png - assets-cg/player/player_attack_right.png - assets-cg/player/player_attack_down.png - assets-cg/player/player_attack_left.png - assets-cg/player/player_damage_up.png - assets-cg/player/player_damage_right.png - assets-cg/player/player_damage_down.png - assets-cg/player/player_damage_left.png + assets-cg/player/player_up.aseprite + assets-cg/player/player_right.aseprite + assets-cg/player/player_down.aseprite + assets-cg/player/player_left.aseprite # Skill animations assets-cg/skills/swing_up.png assets-cg/skills/swing_right.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..401f6a4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +A fun game about smashing monsters in a dungeon. + +Compiling requires py\_aseprite by Eiyeron: [https://github.com/Eiyeron/py\_aseprite](https://github.com/Eiyeron/py_aseprite) diff --git a/assets-cg/converters.py b/assets-cg/converters.py index 3f7d172..bc7aa0a 100644 --- a/assets-cg/converters.py +++ b/assets-cg/converters.py @@ -1,5 +1,6 @@ import fxconv import re +import os.path from PIL import Image, ImageChops @@ -10,6 +11,8 @@ def convert(input, output, params, target): o = convert_level_map(input, params) elif params["custom-type"] == "animation": o = convert_animation(input, params) + elif params["custom-type"] == "aseprite-anim": + o = convert_aseprite_anim(input, output, params) else: recognized = False @@ -164,3 +167,151 @@ def convert_animation(input, params): }) return o + +def convert_aseprite_anim(input, output, params): + from aseprite import AsepriteFile + + #--- + # Perform checks for support on the Aseprite file + #--- + + with open(input, "rb") as fp: + ase = AsepriteFile(fp.read()) + + if ase.header.color_depth != 32: + raise fxconv.FxconvError("Only RGBA supported yet, sorry x_x") + + # Find tags that give names to animations + frame_tags = None + for c in ase.frames[0].chunks: + if c.chunk_type == 0x2018: # Tags + frame_tags = c + + if frame_tags is None: + raise fxconv.FxconvError("Found no frame tags") + + if len(ase.layers) != 1: + raise fxconv.FxconvError("Only one layer supported yet, sorry x_x") + + # Find cel chunks of suitable types in each frame + cel_chunks = [] + for i, f in enumerate(ase.frames): + for c in f.chunks: + if c.chunk_type == 0x2005: # Cel + cel_chunks.append(c) + break + else: + raise fxconv.FxconvError(f"Found no cels for frame #{i+1}") + + if cel_chunks[-1].cel_type not in [0,2]: + raise fxconv.FxconvError(f"Cel chunk for frame #{i+1} is linked " + + "or tilemap") + + #--- + # Print summary of animations + #--- + + print(f"{os.path.basename(input)} ({ase.header.width}x{ase.header.height})", + f"has {len(frame_tags.tags)} animations:") + for t in frame_tags.tags: + name, from_, to = t['name'], t['from'], t['to'] + print(f" '{name}': Frames {from_} to {to}", end="") + durations = [ase.frames[i].frame_duration for i in range(from_, to+1)] + print(" (" + ", ".join(f"{d} ms" for d in durations) + ")") + + #--- + # Generate PIL images for each frame + #--- + + pil_frames = [] + + for i, f in enumerate(ase.frames): + img = Image.new("RGBA", (ase.header.width, ase.header.height)) + c = cel_chunks[i] + pixels = [] + offset = 0 + + for y in range(c.data['height']): + for x in range(c.data['width']): + r, g, b, a = c.data['data'][offset:offset+4] + pixels.append((r, g, b, a)) + offset += 4 + + img.putdata(pixels) + pil_frames.append(img) + + #--- + # Parse parameters + #--- + + if "center" in params: + center = tuple(map(int, params["center"].split(","))) + else: + center = (ase.header.width // 2, ase.header.height // 2) + + if "next" in params: + next_anim = [s.strip() for s in params["next"].split(",")] + next_anim = dict([s.split("=", 1) for s in next_anim]) + else: + next_anim = dict() + + #--- + # Generate compact sheets and object data for each animation + #--- + + bg = Image.new("RGBA", (ase.header.width, ase.header.height), (0, 0, 0, 0)) + + # This will old all animations in sequence + o = fxconv.ObjectData() + sizeof_frame = 16 + + for t in frame_tags.tags: + name, from_, to = t['name'], t['from'], t['to'] + total_width = 0 + total_height = 0 + + for i in range(from_, to+1): + bbox = ImageChops.difference(pil_frames[i], bg).getbbox() + if bbox: + pil_frames[i] = (bbox[0], bbox[1], pil_frames[i].crop(bbox)) + else: + pil_frames[i] = (0, 0, pil_frames[i]) + + total_width += pil_frames[i][2].width + total_height = max(total_height, pil_frames[i][2].height) + + + # Define a new symbol for each animation + symname = params["name"] + "_" + name + s = fxconv.Structure() + + sheet = Image.new("RGBA", (total_width, total_height), (0, 0, 0, 0)) + x = 0 + + for i in range(from_, to+1): + s += fxconv.ref(f"{symname}_sheet") + s += fxconv.u8(x) + fxconv.u8(0) + s += fxconv.u8(pil_frames[i][2].width) + s += fxconv.u8(pil_frames[i][2].height) + s += fxconv.u8(center[0] - pil_frames[i][0]) + s += fxconv.u8(center[1] - pil_frames[i][1]) + s += fxconv.u16(ase.frames[i].frame_duration) + + if i < to: + s += fxconv.ref(symname, (i-from_+1) * sizeof_frame) + elif name in next_anim: + s += fxconv.ref(params["name"] + "_" + next_anim[name]) + else: + s += fxconv.u32(0) + + sheet.paste(pil_frames[i][2], (x, 0)) + x += pil_frames[i][2].width + + o += fxconv.sym(symname) + o += s + o += fxconv.sym(f"{symname}_sheet") + o += fxconv.convert_bopti_cg(sheet, { + "profile": params.get("profile", "p8"), + }) + + return o diff --git a/assets-cg/player/fxconv-metadata.txt b/assets-cg/player/fxconv-metadata.txt index f1a8e82..f1c0ff2 100644 --- a/assets-cg/player/fxconv-metadata.txt +++ b/assets-cg/player/fxconv-metadata.txt @@ -1,52 +1,6 @@ -*.png: - custom-type: animation - name_regex: (.*)\.png anim_\1 +player_*.aseprite: + custom-type: aseprite-anim + name_regex: (.*)\.aseprite anims_\1 + center: 12, 17 + next: Idle=Idle, Walking=Walking, Hit=Idle, Attack=Idle profile: p8 - -player_idle_*.png: - frame_duration: 500, 500 - center: 12, 17 - -player_idle_up.png: - next: anim_player_idle_up - -player_idle_right.png: - next: anim_player_idle_right - -player_idle_down.png: - next: anim_player_idle_down - -player_idle_left.png: - next: anim_player_idle_left - -player_attack_*.png: - frame_duration: 330 - center: 12, 17 - -player_attack_down.png: - next: anim_player_idle_down - -player_attack_up.png: - next: anim_player_idle_up - -player_attack_right.png: - next: anim_player_idle_right - -player_attack_left.png: - next: anim_player_idle_left - -player_damage_*.png: - frame_duration: 90 - center: 12, 17 - -player_damage_down.png: - next: anim_player_idle_down - -player_damage_up.png: - next: anim_player_idle_up - -player_damage_right.png: - next: anim_player_idle_right - -player_damage_left.png: - next: anim_player_idle_left diff --git a/assets-cg/player/player_attack_down.aseprite b/assets-cg/player/player_attack_down.aseprite deleted file mode 100644 index 3fa1638..0000000 Binary files a/assets-cg/player/player_attack_down.aseprite and /dev/null differ diff --git a/assets-cg/player/player_attack_down.png b/assets-cg/player/player_attack_down.png deleted file mode 100644 index ca44d9b..0000000 Binary files a/assets-cg/player/player_attack_down.png and /dev/null differ diff --git a/assets-cg/player/player_attack_left.aseprite b/assets-cg/player/player_attack_left.aseprite deleted file mode 100644 index 96bc5b7..0000000 Binary files a/assets-cg/player/player_attack_left.aseprite and /dev/null differ diff --git a/assets-cg/player/player_attack_left.png b/assets-cg/player/player_attack_left.png deleted file mode 100644 index f73ed3c..0000000 Binary files a/assets-cg/player/player_attack_left.png and /dev/null differ diff --git a/assets-cg/player/player_attack_right.aseprite b/assets-cg/player/player_attack_right.aseprite deleted file mode 100644 index b71338d..0000000 Binary files a/assets-cg/player/player_attack_right.aseprite and /dev/null differ diff --git a/assets-cg/player/player_attack_right.png b/assets-cg/player/player_attack_right.png deleted file mode 100644 index ab06b04..0000000 Binary files a/assets-cg/player/player_attack_right.png and /dev/null differ diff --git a/assets-cg/player/player_attack_up.aseprite b/assets-cg/player/player_attack_up.aseprite deleted file mode 100644 index 33f850d..0000000 Binary files a/assets-cg/player/player_attack_up.aseprite and /dev/null differ diff --git a/assets-cg/player/player_attack_up.png b/assets-cg/player/player_attack_up.png deleted file mode 100644 index de338de..0000000 Binary files a/assets-cg/player/player_attack_up.png and /dev/null differ diff --git a/assets-cg/player/player_damage_down.aseprite b/assets-cg/player/player_damage_down.aseprite deleted file mode 100644 index 7203ad1..0000000 Binary files a/assets-cg/player/player_damage_down.aseprite and /dev/null differ diff --git a/assets-cg/player/player_damage_down.png b/assets-cg/player/player_damage_down.png deleted file mode 100644 index a0ccd06..0000000 Binary files a/assets-cg/player/player_damage_down.png and /dev/null differ diff --git a/assets-cg/player/player_damage_left.aseprite b/assets-cg/player/player_damage_left.aseprite deleted file mode 100644 index b5147a4..0000000 Binary files a/assets-cg/player/player_damage_left.aseprite and /dev/null differ diff --git a/assets-cg/player/player_damage_left.png b/assets-cg/player/player_damage_left.png deleted file mode 100644 index 2a8b862..0000000 Binary files a/assets-cg/player/player_damage_left.png and /dev/null differ diff --git a/assets-cg/player/player_damage_right.aseprite b/assets-cg/player/player_damage_right.aseprite deleted file mode 100644 index 6bc9e7b..0000000 Binary files a/assets-cg/player/player_damage_right.aseprite and /dev/null differ diff --git a/assets-cg/player/player_damage_right.png b/assets-cg/player/player_damage_right.png deleted file mode 100644 index 99e3270..0000000 Binary files a/assets-cg/player/player_damage_right.png and /dev/null differ diff --git a/assets-cg/player/player_damage_up.aseprite b/assets-cg/player/player_damage_up.aseprite deleted file mode 100644 index ac6557f..0000000 Binary files a/assets-cg/player/player_damage_up.aseprite and /dev/null differ diff --git a/assets-cg/player/player_damage_up.png b/assets-cg/player/player_damage_up.png deleted file mode 100644 index a3e3960..0000000 Binary files a/assets-cg/player/player_damage_up.png and /dev/null differ diff --git a/assets-cg/player/player_down.png b/assets-cg/player/player_down.png deleted file mode 100644 index 198d96e..0000000 Binary files a/assets-cg/player/player_down.png and /dev/null differ diff --git a/assets-cg/player/player_idle_down.aseprite b/assets-cg/player/player_idle_down.aseprite deleted file mode 100644 index bb058ac..0000000 Binary files a/assets-cg/player/player_idle_down.aseprite and /dev/null differ diff --git a/assets-cg/player/player_idle_down.png b/assets-cg/player/player_idle_down.png deleted file mode 100644 index f1b4bf5..0000000 Binary files a/assets-cg/player/player_idle_down.png and /dev/null differ diff --git a/assets-cg/player/player_idle_left.aseprite b/assets-cg/player/player_idle_left.aseprite deleted file mode 100644 index bfd4bfc..0000000 Binary files a/assets-cg/player/player_idle_left.aseprite and /dev/null differ diff --git a/assets-cg/player/player_idle_left.png b/assets-cg/player/player_idle_left.png deleted file mode 100644 index 96516ce..0000000 Binary files a/assets-cg/player/player_idle_left.png and /dev/null differ diff --git a/assets-cg/player/player_idle_right.aseprite b/assets-cg/player/player_idle_right.aseprite deleted file mode 100644 index 5205292..0000000 Binary files a/assets-cg/player/player_idle_right.aseprite and /dev/null differ diff --git a/assets-cg/player/player_idle_right.png b/assets-cg/player/player_idle_right.png deleted file mode 100644 index 5576795..0000000 Binary files a/assets-cg/player/player_idle_right.png and /dev/null differ diff --git a/assets-cg/player/player_idle_up.aseprite b/assets-cg/player/player_idle_up.aseprite deleted file mode 100644 index 90ebb1a..0000000 Binary files a/assets-cg/player/player_idle_up.aseprite and /dev/null differ diff --git a/assets-cg/player/player_idle_up.png b/assets-cg/player/player_idle_up.png deleted file mode 100644 index 5817eef..0000000 Binary files a/assets-cg/player/player_idle_up.png and /dev/null differ diff --git a/assets-cg/player/player_left.png b/assets-cg/player/player_left.png deleted file mode 100644 index cb73d0c..0000000 Binary files a/assets-cg/player/player_left.png and /dev/null differ diff --git a/assets-cg/player/player_right.png b/assets-cg/player/player_right.png deleted file mode 100644 index dec01b4..0000000 Binary files a/assets-cg/player/player_right.png and /dev/null differ diff --git a/assets-cg/player/player_up.png b/assets-cg/player/player_up.png deleted file mode 100644 index 268d668..0000000 Binary files a/assets-cg/player/player_up.png and /dev/null differ diff --git a/src/anim.c b/src/anim.c index 46cdf93..f6eae54 100644 --- a/src/anim.c +++ b/src/anim.c @@ -12,12 +12,26 @@ anim_frame_t *name[4] = { \ name ## _up, name ## _right, name ## _down, name ## _left }; -DIRECTIONAL_ANIM(anim_player_idle); -DIRECTIONAL_ANIM(anim_player_attack); -DIRECTIONAL_ANIM(anim_player_damage); +#define ANIM_4DIRECTIONAL(prefix, suffix) \ + extern anim_frame_t prefix ## _up_ ## suffix[]; \ + extern anim_frame_t prefix ## _right_ ## suffix[]; \ + extern anim_frame_t prefix ## _down_ ## suffix[]; \ + extern anim_frame_t prefix ## _left_ ## suffix[]; \ + anim_frame_t *prefix ## _ ## suffix[4] = { \ + prefix ## _up_ ## suffix, \ + prefix ## _right_ ## suffix, \ + prefix ## _down_ ## suffix, \ + prefix ## _left_ ## suffix, \ + }; + DIRECTIONAL_ANIM(anim_swing); DIRECTIONAL_ANIM(anim_impale); +ANIM_4DIRECTIONAL(anims_player, Idle); +ANIM_4DIRECTIONAL(anims_player, Walking); +ANIM_4DIRECTIONAL(anims_player, Attack); +ANIM_4DIRECTIONAL(anims_player, Hit); + /* Animation functions. */ fixed_t (anim_duration)(anim_frame_t const *first_frame) diff --git a/src/anim.h b/src/anim.h index 0dde7f5..6ff13fd 100644 --- a/src/anim.h +++ b/src/anim.h @@ -68,8 +68,9 @@ extern anim_frame_t anim_bat_damage_left[]; extern anim_frame_t anim_bat_damage_right[]; /* Quadri-directional animations. */ -extern anim_frame_t *anim_player_idle[4]; -extern anim_frame_t *anim_player_attack[4]; -extern anim_frame_t *anim_player_damage[4]; +extern anim_frame_t *anims_player_Idle[4]; +extern anim_frame_t *anims_player_Walking[4]; +extern anim_frame_t *anims_player_Attack[4]; +extern anim_frame_t *anims_player_Hit[4]; extern anim_frame_t *anim_swing[4]; extern anim_frame_t *anim_impale[4]; diff --git a/src/entities.c b/src/entities.c index 3673de4..1b5243b 100644 --- a/src/entities.c +++ b/src/entities.c @@ -155,7 +155,7 @@ int entity_damage(entity_t *e, int base_damage) entity_set_anim(e, enemies[e->identity]->anim_damage[index]); } else { - entity_set_anim(e, anim_player_damage[e->movement.facing]); + entity_set_anim(e, anims_player_Hit[e->movement.facing]); } return damage; diff --git a/src/main.c b/src/main.c index 7697040..ae58a6b 100644 --- a/src/main.c +++ b/src/main.c @@ -104,7 +104,7 @@ int main(void) player->sprite = (frect_t){ -fix(6)/16, fix(5)/16, -fix(12)/16, fix(4)/16 }; - entity_set_anim(player, anim_player_idle); + entity_set_anim(player, anims_player_Idle); //--- // Main loop @@ -359,7 +359,7 @@ int main(void) bool set_anim = (player->movement.facing != next.facing); game_try_move_entity(&game, player, &next); - if(set_anim) entity_set_anim(player, anim_player_idle); + if(set_anim) entity_set_anim(player, anims_player_Walking); } /* Directions to reach the player from anywhere on the grid */ @@ -434,7 +434,7 @@ int main(void) player->movement.facing); game_add_effect_area(&game, area); - entity_set_anim(player, anim_player_attack); + entity_set_anim(player, anims_player_Attack); player->current_attack = area; player->attack_follows_movement = true; playerd->combo_delay = area->lifetime;