func awakeningsTableHandler(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "<html><head><title>All Awakenings</title></head><body>\n")
	io.WriteString(w, "<table><thead><tr><th>From Card</th><th>To Card</th><th>Chance</th><th>Orb</th><th>Large</th><th>Medium</th><th>Small</th></tr></thead><tbody>\n")
	for _, value := range VcData.Awakenings {
		baseCard := vc.CardScan(value.BaseCardId, VcData.Cards)
		resultCard := vc.CardScan(value.ResultCardId, VcData.Cards)
		fmt.Fprintf(w,
			"<tr><td><img src=\"/images/cardthumb/%s\"/><br /><a href=\"/cards/detail/%d\">%s</a></td><td><img src=\"/images/cardthumb/%s\"/><br /><a href=\"/cards/detail/%d\">%s</a></td><td>%d%%</td><td>%d</td><td>%d</td><td>%d</td><td>%d</td></tr>",
			baseCard.Image(),
			baseCard.Id,
			baseCard.Name,
			resultCard.Image(),
			resultCard.Id,
			resultCard.Name,
			value.Percent,
			value.Material1Count,
			value.Material2Count,
			value.Material3Count,
			value.Material4Count,
		)
	}
	io.WriteString(w, "</tbody></table>\n")
	io.WriteString(w, "</body></html>")
}
func awakeningsCsvHandler(w http.ResponseWriter, r *http.Request) {
	// File header
	w.Header().Set("Content-Disposition", "attachment; filename=vcData-awaken-"+strconv.Itoa(VcData.Version)+"_"+VcData.Common.UnixTime.Format(time.RFC3339)+".csv")
	w.Header().Set("Content-Type", "text/csv")
	cw := csv.NewWriter(w)
	cw.Write([]string{
		"Id",
		"Name",
		"BaseCardId",
		"ResultCardId",
		"Percent",
		"Material1Item ",
		"Material1Count",
		"Material2Item ",
		"Material2Count",
		"Material3Item ",
		"Material3Count",
		"Material4Item ",
		"Material4Count",
		"Order",
		"IsClosed",
	})
	for _, value := range VcData.Awakenings {
		baseCard := vc.CardScan(value.BaseCardId, VcData.Cards)
		cw.Write([]string{
			strconv.Itoa(value.Id),
			baseCard.Name,
			strconv.Itoa(value.BaseCardId),
			strconv.Itoa(value.ResultCardId),
			strconv.Itoa(value.Percent),
			strconv.Itoa(value.Material1Item),
			strconv.Itoa(value.Material1Count),
			strconv.Itoa(value.Material2Item),
			strconv.Itoa(value.Material2Count),
			strconv.Itoa(value.Material3Item),
			strconv.Itoa(value.Material3Count),
			strconv.Itoa(value.Material4Item),
			strconv.Itoa(value.Material4Count),
			strconv.Itoa(value.Order),
			strconv.Itoa(value.IsClosed),
		})
	}
	cw.Flush()
}
func getWikiReward(reward vc.RankRewardSheet, newline bool) string {
	rlist := "%s x%d"
	if newline {
		rlist = "<br />" + rlist
	}

	var r string
	if reward.CardId > 0 {
		card := vc.CardScan(reward.CardId, VcData.Cards)
		if card == nil {
			r = "{{Card Icon|Unknown Card Id}}"
		} else {
			r = fmt.Sprintf("{{Card Icon|%s}}", card.Name)
		}
	} else if reward.ItemId > 0 {
		item := vc.ItemScan(reward.ItemId, VcData.Items)
		if item == nil {
			r = fmt.Sprintf("__UNKNOWN_ITEM_ID:%d__", reward.ItemId)
		} else if item.GroupId == 17 {
			// tickets
			r = fmt.Sprintf("{{Ticket|%s}}", cleanTicketName(item.NameEng))
		} else if item.GroupId == 30 ||
			(item.GroupId >= 10 &&
				item.GroupId <= 16) {
			// Arcana
			r = fmt.Sprintf("{{Arcana|%s}}", cleanArcanaName(item.NameEng))
		} else if (item.GroupId >= 5 && item.GroupId <= 7) || item.GroupId == 31 || item.GroupId == 22 {
			// sword, shoe, key, rod, potion
			r = fmt.Sprintf("{{Valkyrie|%s}}", cleanItemName(item.NameEng))
		} else if item.GroupId == 18 {
			// exchange items
			r = fmt.Sprintf("[[File:%[1]s.png|28px|link=Items#%[1]s]] [[Items#%[1]s|%[1]s]]", item.NameEng)
		} else {
			r = fmt.Sprintf("__UNKNOWN_GROUP:_%d_%s__", item.GroupId, item.NameEng)
		}
	} else {
		r = "Unknown Reward Type"
	}

	return fmt.Sprintf(rlist, r, reward.Num)
}
func eventDetailHandler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	var pathLen int
	if path[len(path)-1] == '/' {
		pathLen = len(path) - 1
	} else {
		pathLen = len(path)
	}

	pathParts := strings.Split(path[1:pathLen], "/")
	// "events/detail/id"
	if len(pathParts) < 3 {
		http.Error(w, "Invalid event id ", http.StatusNotFound)
		return
	}
	eventId, err := strconv.Atoi(pathParts[2])
	if err != nil || eventId < 1 {
		http.Error(w, "Invalid event id "+pathParts[2], http.StatusNotFound)
		return
	}

	event := vc.EventScan(eventId, VcData.Events)

	var prevEvent, nextEvent *vc.Event = nil, nil

	for i := event.Id - 1; i > 0; i-- {
		tmp := vc.EventScan(i, VcData.Events)
		if tmp != nil && tmp.EventTypeId == event.EventTypeId {
			prevEvent = tmp
			break
		}
	}

	prevEventName := ""
	if prevEvent != nil {
		prevEventName = strings.Replace(prevEvent.Name, "【New Event】", "", -1)
	}

	for i := event.Id + 1; i <= vc.MaxEventId(VcData.Events); i++ {
		tmp := vc.EventScan(i, VcData.Events)
		if tmp != nil && tmp.EventTypeId == event.EventTypeId {
			nextEvent = tmp
			break
		}
	}

	nextEventName := ""
	if nextEvent != nil {
		nextEventName = strings.Replace(nextEvent.Name, "【New Event】", "", -1)
	}

	fmt.Fprintf(w, "<html><head><title>%s</title></head><body><h1>%[1]s</h1>\n", event.Name)
	if event.BannerId > 0 {
		fmt.Fprintf(w, `<a href="/images/event/largeimage/%[1]d/event_image_en?filename=Banner_%[2]s.png"><img src="/images/event/largeimage/%[1]d/event_image_en" alt="Banner"/></a><br />`, event.BannerId, url.QueryEscape(event.Name))
	}
	if event.TexIdImage > 0 {
		fmt.Fprintf(w, `<a href="/images/event/largeimage/%[1]d/event_image_en?filename=Banner_%[2]s.png"><img src="/images/event/largeimage/%[1]d/event_image_en" alt="Texture Image" /></a>br />`, event.TexIdImage, url.QueryEscape(event.Name))
	}
	if event.TexIdImage2 > 0 {
		fmt.Fprintf(w, `<a href="/images/event/largeimage/%[1]d/event_image_en?filename=Banner_%[2]s.png"><img src="/images/event/largeimage/%[1]d/event_image_en" alt="Texture Image 2" /></a><br />`, event.TexIdImage2, url.QueryEscape(event.Name))
	}
	if prevEventName != "" {
		fmt.Fprintf(w, "<div style=\"float:left\"><a href=\"%d\">%s</a>\n</div>", prevEvent.Id, prevEventName)
	}
	if nextEventName != "" {
		fmt.Fprintf(w, "<div style=\"float:right\"><a href=\"%d\">%s</a>\n</div>", nextEvent.Id, nextEventName)
	}
	fmt.Fprintf(w, "<div style=\"clear:both;float:left\">Edit on the <a href=\"https://valkyriecrusade.wikia.com/wiki/%s?action=edit\">wikia</a>\n<br />", strings.Replace(event.Name, "【New Event】", "", -1))
	if event.MapId > 0 {
		fmt.Fprintf(w, "<a href=\"/maps/%d\">Map Information</a>\n<br />", event.MapId)
	}
	io.WriteString(w, "<textarea style=\"width:800px;height:760px\">")
	switch event.EventTypeId {
	case 1: // archwitch event
		rtrend := genWikiRankTrend(event)

		var legendary string
		var faws string
		var aws string

		for _, aw := range event.Archwitches(VcData) {
			cardMaster := vc.CardScan(aw.CardMasterId, VcData.Cards)
			if aw.IsLAW() {
				legendary = cardMaster.Name
			} else if aw.IsFAW() {
				faws += "|" + cardMaster.Name + "|Fantasy Archwitch\n"
			} else {
				aws += "|" + cardMaster.Name + "|Archwitch\n"
			}
		}

		eventMap := event.Map(VcData)
		var eHallStart string
		if eventMap == nil || eventMap.ElementalhallStart.IsZero() || event.EndDatetime.Before(eventMap.ElementalhallStart.Time) {
			eHallStart = ""
		} else {
			eHallStart = eventMap.ElementalhallStart.Format(wikiFmt)
		}

		midrewards := ""
		finalrewards := ""
		rankReward := ""
		rr := event.RankRewards(VcData)
		if rr != nil {
			mid := rr.MidRewards(VcData)
			if mid != nil {
				midCaption := fmt.Sprintf("Mid Rankings<br /><small> Cutoff@ %s (JST)</small>",
					rr.MidBonusDistributionDate.Format(wikiFmt),
				)
				midrewards = genWikiRewards(mid, midCaption)
			}
			finalRewardList := rr.FinalRewards(VcData)
			finalrewards = genWikiRewards(finalRewardList, "Final Rankings")
			for _, fr := range finalRewardList {
				if fr.CardId > 0 {
					rrCard := vc.CardScan(fr.CardId, VcData.Cards)
					rankReward = rrCard.Name
					break
				}
			}
		}

		fmt.Fprintf(w, getEventTemplate(event.EventTypeId),
			event.StartDatetime.Format(wikiFmt), // start
			event.EndDatetime.Format(wikiFmt),   // end
			eHallStart,                          // E-Hall opening
			"1",                                 // E-Hall rotation
			rankReward,                          // rank reward
			legendary,                           // legendary archwitch
			faws,                                // Fantasy Archwitch
			aws,                                 // Regular Archwitch
			html.EscapeString(strings.Replace(event.Description, "\n", "\n\n", -1)),
			(midrewards + finalrewards), //rewards
			rtrend,        // Rank trend
			"",            // sub event (Alliance Battle)
			prevEventName, //Previous event name
			nextEventName, // next event name
		)
	case 16: // alliance bingo battle
		fallthrough
	case 11: // special campaign (Abyssal AW and others)
		// may just do the THOR event seprately and leave this as just news
		fallthrough
	case 13: //Alliance Ultimate Battle
		fallthrough
	case 12: // Alliance Duel
		fallthrough
	case 10: //Alliance Battle
		fallthrough
	default:
		io.WriteString(w, html.EscapeString(strings.Replace(event.Description, "\n", "\n\n", -1)))
	}
	io.WriteString(w, "</textarea></div>")

	io.WriteString(w, "</body></html>")

}
func cardDetailHandler(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	var pathLen int
	if path[len(path)-1] == '/' {
		pathLen = len(path) - 1
	} else {
		pathLen = len(path)
	}

	pathParts := strings.Split(path[1:pathLen], "/")
	// "cards/detail/id"
	if len(pathParts) < 3 {
		http.Error(w, "Invalid card id ", http.StatusNotFound)
		return
	}
	cardId, err := strconv.Atoi(pathParts[2])
	if err != nil || cardId < 1 || cardId > len(VcData.Cards) {
		http.Error(w, "Invalid card id "+pathParts[2], http.StatusNotFound)
		return
	}

	card := vc.CardScan(cardId, VcData.Cards)
	evolutions := getEvolutions(card)
	amalgamations := getAmalgamations(evolutions)

	lastEvo, ok := evolutions["H"]
	if ok {
		delete(evolutions, "H")
	}
	firstEvo, ok := evolutions["F"]
	if ok {
		delete(evolutions, "F")
	} else {
		firstEvo, ok = evolutions["0"]
		if !ok {
			firstEvo = evolutions["1"]
		}
	}

	var turnOverTo, turnOverFrom *vc.Card
	if firstEvo.Id > 0 {
		if firstEvo.TransCardId > 0 {
			turnOverTo = firstEvo.EvoAccident(VcData.Cards)
		} else {
			turnOverFrom = firstEvo.EvoAccidentOf(VcData.Cards)
		}
	}

	var avail string

	for _, av := range amalgamations {
		// check to see if this card is the result of an amalgamation
		resultAmalgFound := false
		if !resultAmalgFound {
			for _, ev := range evolutions {
				if ev.Id == av.FusionCardId {
					if !strings.Contains(avail, "[[Amalgamation]]") {
						avail += " [[Amalgamation]]"
					}
					resultAmalgFound = true
					break
				}
			}
		}
		// look for a self amalgamation
		if _, ok := evolutions["A"]; !ok {
			var materialFuseId int
			for _, ev := range evolutions {
				switch ev.Id {
				case av.Material1, av.Material2, av.Material3, av.Material4:
					materialFuseId = av.FusionCardId
				}
			}
			if materialFuseId > 0 {
				fuseCard := vc.CardScan(materialFuseId, VcData.Cards)
				if fuseCard.Name == card.Name {
					evolutions["A"] = *fuseCard
					if !strings.Contains(avail, "[[Amalgamation]]") {
						avail += " [[Amalgamation]]"
					}
				}
			}
		}
		if _, ok := evolutions["A"]; ok && resultAmalgFound {
			break
		}
	}

	skipFirstEvo := false
	if gevo, ok := evolutions["G"]; ok {
		skipFirstEvo = gevo.Id == firstEvo.Id
	}
	//skipFirstEvo = skipFirstEvo || (firstEvo.Id == lastEvo.Id && firstEvo.Rarity() != "X")
	//fmt.Fprintf(os.Stdout, "Skip First Evo: %v\n", skipFirstEvo)

	cardName := card.Name
	if len(cardName) == 0 {
		cardName = firstEvo.Image()
	}

	fmt.Fprintf(w, "<html><head><title>%s</title></head><body><h1>%[1]s</h1>\n", cardName)
	fmt.Fprintf(w, "<div>Edit on the <a href=\"https://valkyriecrusade.wikia.com/wiki/%s?action=edit\">wikia</a>\n<br />", cardName)
	io.WriteString(w, "<textarea readonly=\"readonly\" style=\"width:100%;height:450px\">")
	if card.IsClosed != 0 {
		io.WriteString(w, "{{Unreleased}}")
	}
	fmt.Fprintf(w, "{{Card\n|element = %s\n", card.Element())
	if firstEvo.Id > 0 {
		skillEvoMod := ""
		if firstEvo.Rarity()[0:1] == "G" {
			skillEvoMod = "g"
		}

		fmt.Fprintf(w, "|rarity = %s\n", fixRarity(firstEvo.Rarity()))

		if !skipFirstEvo {
			io.WriteString(w, printWikiSkill(firstEvo.Skill1(VcData), nil, ""))
		}

		skill2 := firstEvo.Skill2(VcData)
		if skill2 != nil {
			io.WriteString(w, printWikiSkill(skill2, lastEvo.Skill2(VcData), skillEvoMod+"2"))
			// print skill 3 if it exists
			io.WriteString(w, printWikiSkill(firstEvo.Skill3(VcData), nil, skillEvoMod+"3"))

		} else if lastEvo.Id > 0 {
			io.WriteString(w, printWikiSkill(lastEvo.Skill2(VcData), nil, skillEvoMod+"2"))
			// print skill 3 if it exists
			io.WriteString(w, printWikiSkill(lastEvo.Skill3(VcData), nil, skillEvoMod+"3"))
		}

		io.WriteString(w, printWikiSkill(lastEvo.ThorSkill1(VcData), nil, skillEvoMod+"t"))
	} else {
		io.WriteString(w, "|rarity = \n|skill = \n|skill lv1 = \n|skill lv10 = \n|procs = \n")
	}
	if evo, ok := evolutions["A"]; ok {
		aSkillName := evo.Skill1Name(VcData)
		if aSkillName != firstEvo.Skill1Name(VcData) {
			io.WriteString(w, printWikiSkill(evo.Skill1(VcData), nil, "a"))
		}
		if _, ok := evolutions["GA"]; ok {
			io.WriteString(w, printWikiSkill(evo.Skill1(VcData), nil, "ga"))
		}
	}
	if evo, ok := evolutions["G"]; ok {
		io.WriteString(w, printWikiSkill(evo.Skill1(VcData), nil, "g"))
		io.WriteString(w, printWikiSkill(evo.Skill2(VcData), nil, "g2"))
		io.WriteString(w, printWikiSkill(evo.Skill3(VcData), nil, "g3"))
		io.WriteString(w, printWikiSkill(evo.ThorSkill1(VcData), nil, "gt"))
	}

	//traverse evolutions in order
	var evokeys []string
	for k := range evolutions {
		evokeys = append(evokeys, k)
	}
	sort.Strings(evokeys)
	for i, k := range evokeys {
		evo := evolutions[k]
		if i == 0 && skipFirstEvo {
			continue
		}
		fmt.Fprintf(w, "|cost %[1]s = %[2]d\n|atk %[1]s = %[3]d / %s\n|def %[1]s = %[5]d / %s\n|soldiers %[1]s = %[7]d / %s\n",
			strings.ToLower(k),
			evo.DeckCost,
			evo.DefaultOffense, maxStatAtk(evo, len(evolutions)),
			evo.DefaultDefense, maxStatDef(evo, len(evolutions)),
			evo.DefaultFollower, maxStatFollower(evo, len(evolutions)))
	}
	fmt.Fprintf(w, "|description = %s\n|friendship = %s\n",
		html.EscapeString(card.Description(VcData)), html.EscapeString(strings.Replace(card.Friendship(VcData), "\n", "<br />", -1)))
	login := card.Login(VcData)
	if len(strings.TrimSpace(login)) > 0 {
		fmt.Fprintf(w, "|login = %s\n", html.EscapeString(strings.Replace(login, "\n", "<br />", -1)))
	}
	fmt.Fprintf(w, "|meet = %s\n|battle start = %s\n|battle end = %s\n|friendship max = %s\n|friendship event = %s\n", html.EscapeString(strings.Replace(card.Meet(VcData), "\n", "<br />", -1)),
		html.EscapeString(strings.Replace(card.BattleStart(VcData), "\n", "<br />", -1)), html.EscapeString(strings.Replace(card.BattleEnd(VcData), "\n", "<br />", -1)),
		html.EscapeString(strings.Replace(card.FriendshipMax(VcData), "\n", "<br />", -1)), html.EscapeString(strings.Replace(card.FriendshipEvent(VcData), "\n", "<br />", -1)))

	var awakenInfo *vc.CardAwaken
	for _, val := range VcData.Awakenings {
		if lastEvo.Id == val.BaseCardId {
			awakenInfo = &val
			break
		}
	}
	if awakenInfo != nil {
		fmt.Fprintf(w, "|awaken chance = %d\n|awaken orb = %d\n|awaken l = %d\n|awaken m = %d\n|awaken s = %d\n",
			awakenInfo.Percent,
			awakenInfo.Material1Count,
			awakenInfo.Material2Count,
			awakenInfo.Material3Count,
			awakenInfo.Material4Count,
		)
	}

	var aw *vc.Archwitch
	for _, evo := range evolutions {
		if nil != evo.Archwitch(VcData) {
			aw = evo.Archwitch(VcData)
			break
		}
	}
	if aw != nil {
		for _, like := range aw.Likeability(VcData) {
			fmt.Fprintf(w, "|likeability %d = %s\n",
				like.Friendship,
				html.EscapeString(strings.Replace(like.Likability, "\n", "<br />", -1)),
			)
		}
	}

	if turnOverFrom != nil {
		fmt.Fprintf(w, "|turnoverfrom = %s\n", turnOverFrom.Name)
	} else if turnOverTo != nil {
		fmt.Fprintf(w, "|turnoverto = %s\n", turnOverTo.Name)
		fmt.Fprintf(w, "|availability = %s\n", avail)
	} else {
		fmt.Fprintf(w, "|availability = %s\n", avail)
	}
	io.WriteString(w, "}}")

	//Write out amalgamations here
	if len(amalgamations) > 0 {
		io.WriteString(w, "\n==''[[Amalgamation]]''==\n")
		for _, v := range amalgamations {
			mats := v.Materials(VcData)
			l := len(mats)
			fmt.Fprintf(w, "{{Amalgamation|matcount = %d\n|name 1 = %s|rarity 1 = %s\n|name 2 = %s|rarity 2 = %s\n|name 3 = %s|rarity 3 = %s\n",
				l-1, mats[0].Name, mats[0].Rarity(), mats[1].Name, mats[1].Rarity(), mats[2].Name, mats[2].Rarity())
			if l > 3 {
				fmt.Fprintf(w, "|name 4 = %s|rarity 4 = %s\n", mats[3].Name, mats[3].Rarity())
			}
			if l > 4 {
				fmt.Fprintf(w, "|name 5 = %s|rarity 5 = %s\n", mats[4].Name, mats[4].Rarity())
			}
			io.WriteString(w, "}}\n")
		}
	}
	io.WriteString(w, "</textarea></div>")
	// show images here
	io.WriteString(w, "<div style=\"float:left\">")
	for _, k := range evokeys {
		evo := evolutions[k]
		fmt.Fprintf(w,
			`<div style="float: left; margin: 3px"><a href="/images/cardthumb/%s"><img src="/images/cardthumb/%[1]s"/></a><br />%s : %s☆</div>`,
			evo.Image(),
			evo.Name,
			k,
		)
	}
	io.WriteString(w, "<div style=\"clear: both\">")
	for _, k := range evokeys {
		evo := evolutions[k]

		if _, err := os.Stat(vcfilepath + "/card/hd/" + evo.Image()); err == nil {
			fmt.Fprintf(w,
				`<div style="float: left; margin: 3px"><a href="/images/cardHD/%s.png"><img src="/images/cardHD/%[1]s.png"/></a><br />%s : %s☆</div>`,
				evo.Image(),
				evo.Name, k)
		} else if _, err := os.Stat(vcfilepath + "/card/md/" + evo.Image()); err == nil {
			fmt.Fprintf(w,
				`<div style="float: left; margin: 3px"><a href="/images/card/%s.png"><img src="/images/card/%[1]s.png"/></a><br />%s : %s☆</div>`,
				evo.Image(),
				evo.Name, k)
		} else {
			fmt.Fprintf(w,
				`<div style="float: left; margin: 3px"><a href="/images/cardSD/%s.png"><img src="/images/cardSD/%[1]s.png"/></a><br />%s : %s☆</div>`,
				evo.Image(),
				evo.Name, k)
		}
	}
	io.WriteString(w, "</div>")
	io.WriteString(w, "</body></html>")
}
func getEvolutions(card *vc.Card) map[string]vc.Card {
	ret := make(map[string]vc.Card)

	// handle cards like Chimrey and Time Traveler (enemy)
	if card.CardCharaId < 1 {
		ret["0"] = *card
		ret["F"] = *card
		ret["H"] = *card
		return ret
	}

	getAmalBaseCard := func(card *vc.Card) map[string]vc.Card {
		if card.IsAmalgamation(VcData.Amalgamations) {
			// check for a base amalgamation with the same name
			// if there is one, use that for the base card
			for _, amal := range card.Amalgamations(VcData) {
				if card.Id == amal.FusionCardId {
					// material 1
					ac := vc.CardScan(amal.Material1, VcData.Cards)
					if ac.Id != card.Id && ac.Name == card.Name {
						return getEvolutions(ac)
					}
					// material 2
					ac = vc.CardScan(amal.Material2, VcData.Cards)
					if ac.Id != card.Id && ac.Name == card.Name {
						return getEvolutions(ac)
					}
					// material 3
					ac = vc.CardScan(amal.Material3, VcData.Cards)
					if ac != nil && ac.Id != card.Id && ac.Name == card.Name {
						return getEvolutions(ac)
					}
					// material 4
					ac = vc.CardScan(amal.Material4, VcData.Cards)
					if ac != nil && ac.Id != card.Id && ac.Name == card.Name {
						return getEvolutions(ac)
					}
				}
			}
		}
		return nil
	}

	// find the lowest evolution and work from there.
	if card.Rarity()[0] == 'G' {
		bc := card.AwakensFrom(VcData)
		if bc != nil {
			// look for self amalgamation (like sulis)
			amalBaseCard := getAmalBaseCard(bc)
			if amalBaseCard != nil {
				return amalBaseCard
			}
			return getEvolutions(bc)
		}
	} else if card.EvolutionRank != 0 {
		// check for a previous evolution
		for _, c := range VcData.Cards {
			if c.EvolutionCardId == card.Id {
				return getEvolutions(&c)
			}
		}
	} else {
		// check for self amalgamation (like sulis)
		amalBaseCard := getAmalBaseCard(card)
		if amalBaseCard != nil {
			return amalBaseCard
		}
	}

	// get the actual evolution list

	ret[strconv.Itoa(card.EvolutionRank)] = *card
	ret["F"] = *card
	nextId := card.EvolutionCardId
	lastEvo := card
	for nextId > 1 {
		nextCard := vc.CardScan(nextId, VcData.Cards)
		// verify that we haven't switched characters like Terra -> Rhea
		if card.CardCharaId == nextCard.CardCharaId {
			nextEvo := strconv.Itoa(nextCard.EvolutionRank)
			ret[nextEvo] = *nextCard
			lastEvo = nextCard
		}
		nextId = nextCard.EvolutionCardId
	}
	ret["H"] = *lastEvo

	// check if the card has a known awakening:
	cardg := lastEvo.AwakensTo(VcData)
	// this doesn't mean that the card has an awakening, it just makes it easier to find
	if cardg != nil {
		ret["G"] = *cardg
	} else {
		// look for the awakening the hard way now, based on the character id
		gs := 0
		for _, val := range VcData.Cards {
			if card.CardCharaId == val.CardCharaId && val.Rarity()[0] == 'G' {
				if gs == 0 {
					ret["G"] = val
				} else {
					ret["G"+strconv.Itoa(gs)] = val
				}
				gs++
			}
		}
	}

	return ret
}