~slendi/techmates

6e4db16dc8cc5b3fff017bd34777ac0272da52df — Slendi 2 months ago 7031ea0
Add spritesheet and INI parsing

Signed-off-by: Slendi <slendi@socopon.com>
4 files changed, 229 insertions(+), 0 deletions(-)

M main.odin
A settings.ini
A spritesheet.aseprite
A spritesheet.png
M main.odin => main.odin +200 -0
@@ 1,12 1,212 @@
package main

import "base:runtime"
import "core:encoding/ini"
import "core:fmt"
import "core:math"
import path "core:path/filepath"
import "core:strconv"
import "core:strings"

import win "core:sys/windows"
import sdl "vendor:sdl2"
import sdl_img "vendor:sdl2/image"

Vec2 :: [2]int

FrameLayout :: enum {
	Column,
	Row,
}

Anim :: struct {
	frame_layout:    FrameLayout,
	fps:             f64,
	frames:          int,
	size, grab, off: Vec2,
}

Character :: struct {
	spritesheet, name: string,
	animations:        map[string]Anim,
}

ini_value_or_common :: proc(m: ini.Map, section, key: string) -> (string, bool) {
	value, ok := m[section][key]
	if !ok do return m["common"][key]
	return value, true
}

Character_delete :: proc(ch: ^Character) -> runtime.Allocator_Error {
	return delete(ch.animations)
}

Character_load :: proc(dir: string = ".") -> (ch: Character, ok: bool) {
	ini_path := path.join({dir, "settings.ini"})
	defer delete(ini_path)

	ini, ini_err, ok_ini := ini.load_map_from_path(
		ini_path,
		context.allocator,
		{key_lower_case = true},
	)
	if !ok_ini {
		fmt.println("ERROR: Failed to read INI file:", ini_err)
		return
	}

	fmt.println(ini)

	ch.name = ini["general"]["name"]
	ch.spritesheet = ini["general"]["spritesheet"]

	required_animations := []string{"idle", "walk", "fall"}
	for &anim in required_animations {
		_, ok = ini[anim]
		if !ok {
			fmt.printf("ERROR: Required section {} not found\n", anim)
			return
		}
	}

	animations := []string {
		"idle",
		"walk",
		"walk_left",
		"walk_right",
		"grab",
		"grab_left",
		"grab_right",
		"fall",
		"drag",
		"drag_left",
		"drag_right",
	}
	for anim in animations {
		a := Anim{}
		{ 	// Frame layout
			frame_layout_raw_value, ok := ini_value_or_common(ini, anim, "frame_layout")
			if ok {
				frame_layout_value := strings.to_lower(frame_layout_raw_value)
				defer delete(frame_layout_value)
				a.frame_layout = .Row if frame_layout_value == "row" else .Column
			} else {
				fmt.println("WARNING: No frame layout specified. Defaulting to column.")
				a.frame_layout = .Column
			}
		}

		{ 	// FPS
			fps_value, ok := ini_value_or_common(ini, anim, "fps")
			if !ok {
				fmt.println("ERROR: FPS not specified.")
				return
			}
			a.fps, ok = strconv.parse_f64(fps_value)
			if !ok {
				fmt.println("ERROR: Invalid FPS value specified.")
				return
			}
		}

		{ 	// Frames
			frames_value, ok := ini_value_or_common(ini, anim, "frames")
			if !ok {
				fmt.println("ERROR: Frames not specified.")
				return
			}
			a.frames, ok = strconv.parse_int(frames_value)
			if !ok {
				fmt.println("ERROR: Invalid frames value specified.")
				return
			}
		}

		{ 	// Size
			w_value, ok := ini_value_or_common(ini, anim, "w")
			if !ok {
				fmt.println("ERROR: No width specified")
				return
			}
			a.size.x, ok = strconv.parse_int(w_value)
			if !ok {
				fmt.println("ERROR: Invalid width specified")
				return
			}

			h_value, ok1 := ini_value_or_common(ini, anim, "h")
			if !ok1 {
				fmt.println("ERROR: No height specified")
				return
			}
			a.size.y, ok1 = strconv.parse_int(h_value)
			if !ok1 {
				fmt.println("ERROR: Invalid height specified")
				return
			}
		}

		{ 	// Offset
			x_value, ok := ini_value_or_common(ini, anim, "offx")
			if !ok {
				fmt.println("ERROR: No X offset specified")
				return
			}
			a.off.x, ok = strconv.parse_int(x_value)
			if !ok {
				fmt.println("ERROR: Invalid X offset specified")
				return
			}

			y_value, ok1 := ini_value_or_common(ini, anim, "offy")
			a.off.y, ok1 = strconv.parse_int(y_value)
			if !ok1 {
				fmt.println("ERROR: Invalid Y offset specified")
				return
			}
		}

		if strings.has_prefix(anim, "grab") || strings.has_prefix(anim, "drag") {
			// Grab offset
			x_value, ok := ini_value_or_common(ini, anim, "grabx")
			if !ok {
				fmt.println("ERROR: No X grab offset specified")
				return
			}
			a.grab.x, ok = strconv.parse_int(x_value)
			if !ok {
				fmt.println("ERROR: Invalid X grab offset specified")
				return
			}

			y_value, ok1 := ini_value_or_common(ini, anim, "graby")
			if !ok1 {
				fmt.println("ERROR: No Y grab offset specified")
				return
			}
			a.grab.y, ok1 = strconv.parse_int(y_value)
			if !ok1 {
				fmt.println("ERROR: Invalid Y grab offset specified")
				return
			}
		}

		fmt.println(anim)
		ch.animations[anim] = a
	}

	ok = true
	return
}

main :: proc() {
	ch, ok := Character_load()
	if !ok {
		panic("Invalid character")
	}

	fmt.println(ch)

	desktop_width := win.GetSystemMetrics(win.SM_CXSCREEN)
	desktop_height := win.GetSystemMetrics(win.SM_CYSCREEN)


A settings.ini => settings.ini +29 -0
@@ 0,0 1,29 @@
[general]
name = StickMan
spritesheet = spritesheet.png

[common]
w = 32
h = 32
frame_layout = column
fps = 5
frames = 2
grabx = 15
gtaby = 11
offy = 0

[idle]
offx = 0
frames = 2

[walk]
offx = 32

[grab]
offx = 64

[fall]
offx = 96

[drag]
offx = 128

A spritesheet.aseprite => spritesheet.aseprite +0 -0
A spritesheet.png => spritesheet.png +0 -0