diff --git a/frontend/angular.json b/frontend/angular.json
index 6b61f22..471c7fc 100644
--- a/frontend/angular.json
+++ b/frontend/angular.json
@@ -75,7 +75,8 @@
"browserTarget": "legendsbrowser:build:production"
},
"development": {
- "browserTarget": "legendsbrowser:build:development"
+ "browserTarget": "legendsbrowser:build:development",
+ "proxyConfig": "src/proxy.conf.json"
}
},
"defaultConfiguration": "development"
@@ -108,4 +109,4 @@
}
},
"defaultProject": "legendsbrowser"
-}
+}
\ No newline at end of file
diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts
index 0297262..e7f1cae 100644
--- a/frontend/src/app/app-routing.module.ts
+++ b/frontend/src/app/app-routing.module.ts
@@ -1,7 +1,13 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
+import { EntitiesResolver, EntityResolver } from './entity.service';
+import { CivilizationsComponent } from './pages/civilizations/civilizations.component';
+import { EntityComponent } from './pages/entity/entity.component';
-const routes: Routes = [];
+const routes: Routes = [
+ { path: '', component: CivilizationsComponent, resolve: { civilizations: EntitiesResolver } },
+ { path: 'entity/:id', component: EntityComponent, resolve: { entity: EntityResolver } },
+];
@NgModule({
imports: [RouterModule.forRoot(routes)],
diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html
index e11ca59..0d7d771 100644
--- a/frontend/src/app/app.component.html
+++ b/frontend/src/app/app.component.html
@@ -1,484 +1,2 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ title }} app is running!
-
-
-
-
-
-
-
Resources
-
Here are some links to help you get started:
-
-
-
-
-
Next Steps
-
What do you want to do next with your app?
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
ng generate component xyz
-
ng add @angular/material
-
ng add @angular/pwa
-
ng add _____
-
ng test
-
ng build
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+LegendsBrowser 2
+
\ No newline at end of file
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index b1c6c96..20ddb12 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -1,15 +1,20 @@
+import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
-
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
+import { CivilizationsComponent } from './pages/civilizations/civilizations.component';
+import { EntityComponent } from './pages/entity/entity.component';
@NgModule({
declarations: [
- AppComponent
+ AppComponent,
+ CivilizationsComponent,
+ EntityComponent
],
imports: [
BrowserModule,
+ HttpClientModule,
AppRoutingModule
],
providers: [],
diff --git a/frontend/src/app/entity.service.ts b/frontend/src/app/entity.service.ts
new file mode 100644
index 0000000..8fae019
--- /dev/null
+++ b/frontend/src/app/entity.service.ts
@@ -0,0 +1,46 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, Resolve, RouterStateSnapshot } from '@angular/router';
+import { firstValueFrom, Observable } from 'rxjs';
+import { Entity } from './types';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class EntityService {
+
+ constructor(private http: HttpClient) { }
+
+ getAll(): Promise {
+ return firstValueFrom(this.http.get("./api/entity"));
+ }
+
+ getOne(id: string | number): Promise {
+ return firstValueFrom(this.http.get("./api/entity/" + id));
+ }
+
+}
+
+@Injectable({ providedIn: 'root' })
+export class EntitiesResolver implements Resolve {
+ constructor(private service: EntityService) { }
+
+ resolve(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+ ): Observable | Promise | Entity[] {
+ return this.service.getAll();
+ }
+}
+
+@Injectable({ providedIn: 'root' })
+export class EntityResolver implements Resolve {
+ constructor(private service: EntityService) { }
+
+ resolve(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+ ): Observable | Promise | Entity {
+ return this.service.getOne(route.paramMap.get('id')!);
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/pages/civilizations/civilizations.component.html b/frontend/src/app/pages/civilizations/civilizations.component.html
new file mode 100644
index 0000000..7b454fc
--- /dev/null
+++ b/frontend/src/app/pages/civilizations/civilizations.component.html
@@ -0,0 +1,4 @@
+civilizations works!
+
\ No newline at end of file
diff --git a/frontend/src/app/pages/civilizations/civilizations.component.scss b/frontend/src/app/pages/civilizations/civilizations.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/app/pages/civilizations/civilizations.component.spec.ts b/frontend/src/app/pages/civilizations/civilizations.component.spec.ts
new file mode 100644
index 0000000..0b9d47a
--- /dev/null
+++ b/frontend/src/app/pages/civilizations/civilizations.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CivilizationsComponent } from './civilizations.component';
+
+describe('CivilizationsComponent', () => {
+ let component: CivilizationsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ CivilizationsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CivilizationsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/pages/civilizations/civilizations.component.ts b/frontend/src/app/pages/civilizations/civilizations.component.ts
new file mode 100644
index 0000000..5ad44c9
--- /dev/null
+++ b/frontend/src/app/pages/civilizations/civilizations.component.ts
@@ -0,0 +1,21 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Entity } from 'src/app/types';
+
+@Component({
+ selector: 'app-civilizations',
+ templateUrl: './civilizations.component.html',
+ styleUrls: ['./civilizations.component.scss']
+})
+export class CivilizationsComponent implements OnInit {
+
+ civilizations: Entity[] = [];
+
+ constructor(private route: ActivatedRoute) { }
+
+ ngOnInit(): void {
+ this.civilizations = this.route.snapshot.data['civilizations'];
+ this.civilizations = this.civilizations.filter(c => c.name.length > 0)
+ }
+
+}
diff --git a/frontend/src/app/pages/entity/entity.component.html b/frontend/src/app/pages/entity/entity.component.html
new file mode 100644
index 0000000..023b14d
--- /dev/null
+++ b/frontend/src/app/pages/entity/entity.component.html
@@ -0,0 +1 @@
+{{entity.name}}
\ No newline at end of file
diff --git a/frontend/src/app/pages/entity/entity.component.scss b/frontend/src/app/pages/entity/entity.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/src/app/pages/entity/entity.component.spec.ts b/frontend/src/app/pages/entity/entity.component.spec.ts
new file mode 100644
index 0000000..58691d8
--- /dev/null
+++ b/frontend/src/app/pages/entity/entity.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EntityComponent } from './entity.component';
+
+describe('EntityComponent', () => {
+ let component: EntityComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ EntityComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EntityComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/frontend/src/app/pages/entity/entity.component.ts b/frontend/src/app/pages/entity/entity.component.ts
new file mode 100644
index 0000000..b93647b
--- /dev/null
+++ b/frontend/src/app/pages/entity/entity.component.ts
@@ -0,0 +1,20 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Entity } from 'src/app/types';
+
+@Component({
+ selector: 'app-entity',
+ templateUrl: './entity.component.html',
+ styleUrls: ['./entity.component.scss']
+})
+export class EntityComponent implements OnInit {
+
+ entity!: Entity;
+
+ constructor(private route: ActivatedRoute) { }
+
+ ngOnInit(): void {
+ this.entity = this.route.snapshot.data['entity'];
+ }
+
+}
diff --git a/frontend/src/app/types.ts b/frontend/src/app/types.ts
new file mode 100644
index 0000000..32d1174
--- /dev/null
+++ b/frontend/src/app/types.ts
@@ -0,0 +1,4 @@
+export interface Entity {
+ id: number;
+ name: string;
+}
\ No newline at end of file
diff --git a/frontend/src/proxy.conf.json b/frontend/src/proxy.conf.json
new file mode 100644
index 0000000..f0a425f
--- /dev/null
+++ b/frontend/src/proxy.conf.json
@@ -0,0 +1,6 @@
+{
+ "/api": {
+ "target": "http://localhost:8080",
+ "secure": false
+ }
+}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index b58b54b..a0dc5d9 100644
--- a/go.mod
+++ b/go.mod
@@ -4,5 +4,6 @@ go 1.18
require (
github.com/gorilla/mux v1.8.0
+ github.com/iancoleman/strcase v0.2.0
github.com/sa-/slicefunk v0.1.2
)
diff --git a/go.sum b/go.sum
index 8dfc7b9..942fad2 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,6 @@
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
+github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/sa-/slicefunk v0.1.2 h1:uTQiyKCTwc0RKNxXmG6NNlm3J1LF2hKmLgtf9kRAld4=
github.com/sa-/slicefunk v0.1.2/go.mod h1:k0abNpV9EW8LIPl2+Hc9RiKsojKmsUhNNGFyMpjMTCI=
diff --git a/legendsbrowser b/legendsbrowser
new file mode 100755
index 0000000..1bb0ebc
Binary files /dev/null and b/legendsbrowser differ
diff --git a/main.go b/main.go
index 845d1f0..36e4540 100644
--- a/main.go
+++ b/main.go
@@ -1,202 +1,41 @@
package main
import (
- "encoding/json"
- "encoding/xml"
"fmt"
- "io/ioutil"
+ "legendsbrowser/model"
+ "legendsbrowser/server"
"net/http"
- "os"
- "strconv"
"github.com/gorilla/mux"
- sf "github.com/sa-/slicefunk"
)
-type World struct {
- XMLName xml.Name `xml:"df_world"`
- Name string `xml:"name"`
- AltName string `xml:"altname"`
- Regions []Region `xml:"regions>region"`
- UndergroundRegions []UndergroundRegion `xml:"underground_regions>underground_region"`
- Landmasses []Landmass `xml:"landmasses>landmass"`
- Sites []Site `xml:"sites>site"`
- WorldConstructions []WorldConstruction `xml:"world_constructions>world_construction"`
- Artifacts []Artifact `xml:"artifacts>artifact"`
- HistoricalFigures []HistoricalFigure `xml:"historical_figures>historical_figure"`
- HistoricalEvents []HistoricalEvent `xml:"historical_events>historical_event"`
- HistoricalEventCollections []HistoricalEventCollection `xml:"historical_event_collections>historical_event_collection"`
-}
-
-type NamedObject struct {
- Id int `xml:"id" json:"id"`
- Name string `xml:"name" json:"name"`
-}
-
-func (r NamedObject) id() int { return r.Id }
-func (r NamedObject) name() string { return r.Name }
-
-type Named interface {
- id() int
- name() string
-}
-
-type Region struct {
- XMLName xml.Name `xml:"region" json:"-"`
- NamedObject
- Type string `xml:"type" json:"type"`
-}
-
-type UndergroundRegion struct {
- XMLName xml.Name `xml:"underground_region"`
- NamedObject
- Type string `xml:"type" json:"type"`
-}
-
-type Landmass struct {
- XMLName xml.Name `xml:"landmass"`
- NamedObject
-}
-
-type Site struct {
- XMLName xml.Name `xml:"site" json:"-"`
- NamedObject
- Type string `xml:"type" json:"type"`
- Coords string `xml:"coords" json:"coords"`
- Rectangle string `xml:"rectangle" json:"rectangle"`
- Structures []Structure `xml:"structures>structure" json:"structures"`
-}
-
-// func (obj Site) id() int { return obj.Id }
-// func (obj Site) name() string { return obj.Name }
-
-type Structure struct {
- XMLName xml.Name `xml:"structure" json:"-"`
- LocalId int `xml:"local_id" json:"localId"`
- Name string `xml:"name" json:"name"`
- Type string `xml:"type" json:"type"`
-}
-
-type WorldConstruction struct {
- XMLName xml.Name `xml:"world_construction"`
- NamedObject
-}
-
-type Artifact struct {
- XMLName xml.Name `xml:"artifact"`
- NamedObject
- SiteId int `xml:"site_id" json:"siteId"`
-}
-
-type Element struct {
- XMLName xml.Name
- Value string `xml:",innerxml"`
-}
-
-type HistoricalFigure struct {
- XMLName xml.Name `xml:"historical_figure"`
- NamedObject
- Race string `xml:"race" json:"race"`
- Caste string `xml:"caste" json:"caste"`
- Other []Element `xml:",any" json:"-"`
-}
-
-type HistoricalEvent struct {
- XMLName xml.Name `xml:"historical_event"`
- Id int `xml:"id"`
- Year int `xml:"year"`
- Seconds int `xml:"seconds72"`
- Type string `xml:"type"`
-}
-
-type HistoricalEventCollection struct {
- XMLName xml.Name `xml:"historical_event_collection"`
- NamedObject
- StartYear int `xml:"year"`
- StartSeconds int `xml:"seconds72"`
- EndYear int `xml:"end_year"`
- EndSeconds int `xml:"end_seconds72"`
- Type string `xml:"type" json:"type"`
- EventIds []int `xml:"event" json:"eventIds"`
-}
-
-var world World
+var world model.World
func main() {
fmt.Println("Hallo Welt!")
- xmlFile, err := os.Open("region1-00152-01-01-legends.xml")
- if err != nil {
- fmt.Println(err)
- }
+ world.Load("region1-00152-01-01-legends.xml")
+ world.Process()
- fmt.Println("Successfully Opened users.xml")
- defer xmlFile.Close()
-
- byteValue, _ := ioutil.ReadAll(xmlFile)
- fmt.Println(len(byteValue))
-
- err = xml.Unmarshal(byteValue, &world)
- if err != nil {
- fmt.Println(err)
- }
-
- fmt.Printf("Sites: %d\n", len(world.Sites))
- fmt.Printf("artifacts: %d\n", len(world.Artifacts))
- fmt.Printf("events: %d\n", len(world.HistoricalEvents))
- fmt.Printf("collections: %d\n", len(world.HistoricalEventCollections))
- fmt.Printf(" events: %v\n", len(world.HistoricalEventCollections[0].EventIds))
- fmt.Printf("figures: %d\n", len(world.HistoricalFigures))
- fmt.Printf(" len: %d\n", len(world.HistoricalFigures[0].Other))
- // fmt.Printf(" other: %v\n", world.HistoricalFigures[0].Other)
-
- // for i := 0; i < len(world.Regions); i++ {
- // fmt.Println("Regions Name: " + world.Regions[i].Name)
- // }
-
- // for i := 0; i < len(world.Sites); i++ {
- // fmt.Println("Sites Name: " + world.Sites[i].Name)
- // }
+ model.ListOtherElements(&world.HistoricalEvents)
+ // listOtherElements(&world.HistoricalFigures)
router := mux.NewRouter().StrictSlash(true)
- registerResource(router, "region", world.Regions)
- registerResource(router, "undergroundRegion", world.UndergroundRegions)
- registerResource(router, "landmass", world.Landmasses)
- registerResource(router, "site", world.Sites)
- registerResource(router, "worldConstruction", world.WorldConstructions)
- registerResource(router, "artifact", world.Artifacts)
- registerResource(router, "hf", world.HistoricalFigures)
- registerResource(router, "collection", world.HistoricalEventCollections)
+
+ server.RegisterResource(router, "region", world.RegionMap)
+ server.RegisterResource(router, "undergroundRegion", world.UndergroundRegionMap)
+ server.RegisterResource(router, "landmass", world.LandmassMap)
+ server.RegisterResource(router, "site", world.SiteMap)
+ server.RegisterResource(router, "worldConstruction", world.WorldConstructionMap)
+ server.RegisterResource(router, "artifact", world.ArtifactMap)
+ server.RegisterResource(router, "hf", world.HistoricalFigureMap)
+ server.RegisterResource(router, "collection", world.HistoricalEventCollectionMap)
+ server.RegisterResource(router, "entity", world.EntityMap)
+ server.RegisterResource(router, "event", world.HistoricalEventMap)
+
+ spa := server.SpaHandler{StaticPath: "frontend/dist/legendsbrowser", IndexPath: "index.html"}
+ router.PathPrefix("/").Handler(spa)
+
+ fmt.Println("Serving at :8080")
http.ListenAndServe(":8080", router)
}
-
-type Info struct {
- Id int `json:"id"`
- Name string `json:"name"`
-}
-
-func registerResource[T Named](router *mux.Router, resourceName string, resources []T) {
-
- list := func(w http.ResponseWriter, r *http.Request) {
-
- values := sf.Map(resources, func(item T) *Info { return &Info{Id: item.id(), Name: item.name()} })
-
- json.NewEncoder(w).Encode(values)
- }
-
- get := func(w http.ResponseWriter, r *http.Request) {
- id, err := strconv.Atoi(mux.Vars(r)["id"])
- if err != nil {
- fmt.Println(err)
- }
-
- for _, item := range resources {
- if item.id() == id {
- json.NewEncoder(w).Encode(item)
- }
- }
- }
-
- router.HandleFunc(fmt.Sprintf("/%s", resourceName), list).Methods("GET")
- router.HandleFunc(fmt.Sprintf("/%s/{id}", resourceName), get).Methods("GET")
-}
diff --git a/model/analyze.go b/model/analyze.go
new file mode 100644
index 0000000..48dd147
--- /dev/null
+++ b/model/analyze.go
@@ -0,0 +1,84 @@
+package model
+
+import (
+ "fmt"
+ "legendsbrowser/util"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/iancoleman/strcase"
+)
+
+func ListOtherElements[T TypedOthers](items *[]T) {
+ m := make(map[string]map[string]bool)
+ cantInt := make(map[string]bool)
+ isObj := make(map[string]bool)
+ isMultiple := make(map[string]bool)
+ for _, item := range *items {
+ found := make(map[string]bool)
+ for _, el := range item.Others() {
+ if !m[el.XMLName.Local][item.Type()] {
+ if m[el.XMLName.Local] == nil {
+ m[el.XMLName.Local] = map[string]bool{}
+ }
+ m[el.XMLName.Local][item.Type()] = true
+ }
+ _, err := strconv.Atoi(el.Value)
+ if err != nil {
+ cantInt[el.XMLName.Local] = true
+ }
+ if strings.Contains(el.Value, "<") {
+ isObj[el.XMLName.Local] = true
+ }
+ if found[el.XMLName.Local] {
+ isMultiple[el.XMLName.Local] = true
+ }
+ found[el.XMLName.Local] = true
+ }
+ }
+ ks := util.Keys(m)
+ sort.Strings(ks)
+ for _, k := range ks {
+ events := util.Keys(m[k])
+ sort.Strings(events)
+ // fmt.Println(strconv.FormatBool(cantInt[k]) + " - " + k + ": " + strings.Join(events, ", "))
+ var mult string
+ if isMultiple[k] {
+ mult = "[]"
+ } else {
+ mult = ""
+ }
+
+ if isObj[k] {
+ fmt.Printf("// %s object\n", k)
+ } else if cantInt[k] {
+ fmt.Printf("%s *%sstring `xml:\"%s\" json:\"%s,omitempty\"`\n", strcase.ToCamel(k), mult, k, strcase.ToLowerCamel(k))
+ } else {
+ var types []string
+ if util.ContainsAny(k, "entity_id", "enid", "civ_id", "entity_1", "entity_2") {
+ types = append(types, "entity")
+ }
+ if util.ContainsAny(k, "site_id") {
+ types = append(types, "site")
+ }
+ if util.ContainsAny(k, "structure_id") {
+ types = append(types, "structure")
+ }
+ if util.ContainsAny(k, "hfid", "hist_figure_id", "hist_fig_id") {
+ types = append(types, "hf")
+ }
+ if util.ContainsAny(k, "wcid", "wc_id") {
+ types = append(types, "wc")
+ }
+ if util.ContainsAny(k, "artifact_id") {
+ types = append(types, "artifact")
+ }
+ typestr := strings.Join(types, ",")
+ if typestr != "" {
+ typestr = fmt.Sprintf(" legend:\"%s\"", typestr)
+ }
+ fmt.Printf("%s *%sint `xml:\"%s\" json:\"%s,omitempty\"%s`\n", strcase.ToCamel(k), mult, k, strcase.ToLowerCamel(k), typestr)
+ }
+ }
+}
diff --git a/model/events.go b/model/events.go
new file mode 100644
index 0000000..c4948c8
--- /dev/null
+++ b/model/events.go
@@ -0,0 +1,245 @@
+package model
+
+import "encoding/xml"
+
+type HistoricalEvent struct {
+ XMLName xml.Name `xml:"historical_event" json:"-"`
+ Id_ int `xml:"id" json:"id"`
+ Year int `xml:"year" json:"year"`
+ Seconds int `xml:"seconds72" json:"seconds72"`
+ TypedObject
+
+ ASupportMercEnid *int `xml:"a_support_merc_enid" json:"aSupportMercEnid,omitempty" legend:"entity"`
+ AccountShift *int `xml:"account_shift" json:"accountShift,omitempty"`
+ AcquirerEnid *int `xml:"acquirer_enid" json:"acquirerEnid,omitempty" legend:"entity"`
+ AcquirerHfid *int `xml:"acquirer_hfid" json:"acquirerHfid,omitempty" legend:"hf"`
+ Action *string `xml:"action" json:"action,omitempty"`
+ ActorHfid *int `xml:"actor_hfid" json:"actorHfid,omitempty" legend:"hf"`
+ AgreementId *int `xml:"agreement_id" json:"agreementId,omitempty"`
+ Allotment *int `xml:"allotment" json:"allotment,omitempty"`
+ AllotmentIndex *int `xml:"allotment_index" json:"allotmentIndex,omitempty"`
+ AllyDefenseBonus *int `xml:"ally_defense_bonus" json:"allyDefenseBonus,omitempty"`
+ AppointerHfid *int `xml:"appointer_hfid" json:"appointerHfid,omitempty" legend:"hf"`
+ ArrestingEnid *int `xml:"arresting_enid" json:"arrestingEnid,omitempty" legend:"entity"`
+ ArtifactId *int `xml:"artifact_id" json:"artifactId,omitempty" legend:"artifact"`
+ AttackerCivId *int `xml:"attacker_civ_id" json:"attackerCivId,omitempty" legend:"entity"`
+ AttackerGeneralHfid *int `xml:"attacker_general_hfid" json:"attackerGeneralHfid,omitempty" legend:"hf"`
+ AttackerHfid *int `xml:"attacker_hfid" json:"attackerHfid,omitempty" legend:"hf"`
+ AttackerMercEnid *int `xml:"attacker_merc_enid" json:"attackerMercEnid,omitempty" legend:"entity"`
+ BodyState *string `xml:"body_state" json:"bodyState,omitempty"`
+ BuilderHfid *int `xml:"builder_hfid" json:"builderHfid,omitempty" legend:"hf"`
+ BuildingProfileId *int `xml:"building_profile_id" json:"buildingProfileId,omitempty"`
+ Cause *string `xml:"cause" json:"cause,omitempty"`
+ ChangeeHfid *int `xml:"changee_hfid" json:"changeeHfid,omitempty" legend:"hf"`
+ ChangerHfid *int `xml:"changer_hfid" json:"changerHfid,omitempty" legend:"hf"`
+ Circumstance *string `xml:"circumstance" json:"circumstance,omitempty"`
+ CircumstanceId *int `xml:"circumstance_id" json:"circumstanceId,omitempty"`
+ CivEntityId *int `xml:"civ_entity_id" json:"civEntityId,omitempty" legend:"entity"`
+ CivId *int `xml:"civ_id" json:"civId,omitempty" legend:"entity"`
+ Claim *string `xml:"claim" json:"claim,omitempty"`
+ CoconspiratorBonus *int `xml:"coconspirator_bonus" json:"coconspiratorBonus,omitempty"`
+ CompetitorHfid *[]int `xml:"competitor_hfid" json:"competitorHfid,omitempty" legend:"hf"`
+ ConfessedAfterApbArrestEnid *int `xml:"confessed_after_apb_arrest_enid" json:"confessedAfterApbArrestEnid,omitempty" legend:"entity"`
+ ConspiratorHfid *[]int `xml:"conspirator_hfid" json:"conspiratorHfid,omitempty" legend:"hf"`
+ ContactHfid *int `xml:"contact_hfid" json:"contactHfid,omitempty" legend:"hf"`
+ ConvictIsContact *string `xml:"convict_is_contact" json:"convictIsContact,omitempty"`
+ ConvictedHfid *int `xml:"convicted_hfid" json:"convictedHfid,omitempty" legend:"hf"`
+ ConvicterEnid *int `xml:"convicter_enid" json:"convicterEnid,omitempty" legend:"entity"`
+ Coords *string `xml:"coords" json:"coords,omitempty"`
+ CorruptConvicterHfid *int `xml:"corrupt_convicter_hfid" json:"corruptConvicterHfid,omitempty" legend:"hf"`
+ CorruptorHfid *int `xml:"corruptor_hfid" json:"corruptorHfid,omitempty" legend:"hf"`
+ CorruptorIdentity *int `xml:"corruptor_identity" json:"corruptorIdentity,omitempty"`
+ CorruptorSeenAs *string `xml:"corruptor_seen_as" json:"corruptorSeenAs,omitempty"`
+ CreatorHfid *int `xml:"creator_hfid" json:"creatorHfid,omitempty" legend:"hf"`
+ Crime *string `xml:"crime" json:"crime,omitempty"`
+ DSupportMercEnid *int `xml:"d_support_merc_enid" json:"dSupportMercEnid,omitempty" legend:"entity"`
+ DeathPenalty *string `xml:"death_penalty" json:"deathPenalty,omitempty"`
+ DefenderCivId *int `xml:"defender_civ_id" json:"defenderCivId,omitempty" legend:"entity"`
+ DefenderGeneralHfid *int `xml:"defender_general_hfid" json:"defenderGeneralHfid,omitempty" legend:"hf"`
+ DefenderMercEnid *int `xml:"defender_merc_enid" json:"defenderMercEnid,omitempty" legend:"entity"`
+ Delegated *string `xml:"delegated" json:"delegated,omitempty"`
+ DestEntityId *int `xml:"dest_entity_id" json:"destEntityId,omitempty" legend:"entity"`
+ DestSiteId *int `xml:"dest_site_id" json:"destSiteId,omitempty" legend:"site"`
+ DestStructureId *int `xml:"dest_structure_id" json:"destStructureId,omitempty" legend:"structure"`
+ DestroyedStructureId *int `xml:"destroyed_structure_id" json:"destroyedStructureId,omitempty" legend:"structure"`
+ DestroyerEnid *int `xml:"destroyer_enid" json:"destroyerEnid,omitempty" legend:"entity"`
+ Detected *string `xml:"detected" json:"detected,omitempty"`
+ DidNotRevealAllInInterrogation *string `xml:"did_not_reveal_all_in_interrogation" json:"didNotRevealAllInInterrogation,omitempty"`
+ Dispute *string `xml:"dispute" json:"dispute,omitempty"`
+ DoerHfid *int `xml:"doer_hfid" json:"doerHfid,omitempty" legend:"hf"`
+ Entity1 *int `xml:"entity_1" json:"entity1,omitempty" legend:"entity"`
+ Entity2 *int `xml:"entity_2" json:"entity2,omitempty" legend:"entity"`
+ EntityId *int `xml:"entity_id" json:"entityId,omitempty" legend:"entity"`
+ EntityId1 *int `xml:"entity_id_1" json:"entityId1,omitempty" legend:"entity"`
+ EntityId2 *int `xml:"entity_id_2" json:"entityId2,omitempty" legend:"entity"`
+ Exiled *string `xml:"exiled" json:"exiled,omitempty"`
+ ExpelledCreature *[]int `xml:"expelled_creature" json:"expelledCreature,omitempty"`
+ ExpelledHfid *[]int `xml:"expelled_hfid" json:"expelledHfid,omitempty" legend:"hf"`
+ ExpelledNumber *[]int `xml:"expelled_number" json:"expelledNumber,omitempty"`
+ ExpelledPopId *[]int `xml:"expelled_pop_id" json:"expelledPopId,omitempty"`
+ FailedJudgmentTest *string `xml:"failed_judgment_test" json:"failedJudgmentTest,omitempty"`
+ FeatureLayerId *int `xml:"feature_layer_id" json:"featureLayerId,omitempty"`
+ First *string `xml:"first" json:"first,omitempty"`
+ FooledHfid *int `xml:"fooled_hfid" json:"fooledHfid,omitempty" legend:"hf"`
+ FormId *int `xml:"form_id" json:"formId,omitempty"`
+ FramerHfid *int `xml:"framer_hfid" json:"framerHfid,omitempty" legend:"hf"`
+ FromOriginal *string `xml:"from_original" json:"fromOriginal,omitempty"`
+ GamblerHfid *int `xml:"gambler_hfid" json:"gamblerHfid,omitempty" legend:"hf"`
+ GiverEntityId *int `xml:"giver_entity_id" json:"giverEntityId,omitempty" legend:"entity"`
+ GiverHistFigureId *int `xml:"giver_hist_figure_id" json:"giverHistFigureId,omitempty" legend:"hf"`
+ Group1Hfid *int `xml:"group_1_hfid" json:"group1Hfid,omitempty" legend:"hf"`
+ Group2Hfid *[]int `xml:"group_2_hfid" json:"group2Hfid,omitempty" legend:"hf"`
+ GroupHfid *[]int `xml:"group_hfid" json:"groupHfid,omitempty" legend:"hf"`
+ HeldFirmInInterrogation *string `xml:"held_firm_in_interrogation" json:"heldFirmInInterrogation,omitempty"`
+ HfRep1Of2 *string `xml:"hf_rep_1_of_2" json:"hfRep1Of2,omitempty"`
+ HfRep2Of1 *string `xml:"hf_rep_2_of_1" json:"hfRep2Of1,omitempty"`
+ Hfid *[]int `xml:"hfid" json:"hfid,omitempty" legend:"hf"`
+ Hfid1 *int `xml:"hfid1" json:"hfid1,omitempty" legend:"hf"`
+ Hfid2 *int `xml:"hfid2" json:"hfid2,omitempty" legend:"hf"`
+ HfidTarget *int `xml:"hfid_target" json:"hfidTarget,omitempty" legend:"hf"`
+ HistFigId *int `xml:"hist_fig_id" json:"histFigId,omitempty" legend:"hf"`
+ HistFigureId *int `xml:"hist_figure_id" json:"histFigureId,omitempty" legend:"hf"`
+ HonorId *int `xml:"honor_id" json:"honorId,omitempty"`
+ IdentityId *int `xml:"identity_id" json:"identityId,omitempty" legend:"entity"`
+ IdentityId1 *int `xml:"identity_id1" json:"identityId1,omitempty" legend:"entity"`
+ IdentityId2 *int `xml:"identity_id2" json:"identityId2,omitempty" legend:"entity"`
+ ImplicatedHfid *[]int `xml:"implicated_hfid" json:"implicatedHfid,omitempty" legend:"hf"`
+ Inherited *string `xml:"inherited" json:"inherited,omitempty"`
+ InitiatingEnid *int `xml:"initiating_enid" json:"initiatingEnid,omitempty" legend:"entity"`
+ InstigatorHfid *int `xml:"instigator_hfid" json:"instigatorHfid,omitempty" legend:"hf"`
+ Interaction *string `xml:"interaction" json:"interaction,omitempty"`
+ InterrogatorHfid *int `xml:"interrogator_hfid" json:"interrogatorHfid,omitempty" legend:"hf"`
+ JoinEntityId *int `xml:"join_entity_id" json:"joinEntityId,omitempty" legend:"entity"`
+ JoinedEntityId *int `xml:"joined_entity_id" json:"joinedEntityId,omitempty" legend:"entity"`
+ JoinerEntityId *int `xml:"joiner_entity_id" json:"joinerEntityId,omitempty" legend:"entity"`
+ JoiningEnid *[]int `xml:"joining_enid" json:"joiningEnid,omitempty" legend:"entity"`
+ Knowledge *string `xml:"knowledge" json:"knowledge,omitempty"`
+ LastOwnerHfid *int `xml:"last_owner_hfid" json:"lastOwnerHfid,omitempty" legend:"hf"`
+ LeaderHfid *int `xml:"leader_hfid" json:"leaderHfid,omitempty" legend:"hf"`
+ Link *string `xml:"link" json:"link,omitempty"`
+ LureHfid *int `xml:"lure_hfid" json:"lureHfid,omitempty" legend:"hf"`
+ MasterWcid *int `xml:"master_wcid" json:"masterWcid,omitempty" legend:"wc"`
+ Method *string `xml:"method" json:"method,omitempty"`
+ Modification *string `xml:"modification" json:"modification,omitempty"`
+ ModifierHfid *int `xml:"modifier_hfid" json:"modifierHfid,omitempty" legend:"hf"`
+ Mood *string `xml:"mood" json:"mood,omitempty"`
+ NameOnly *string `xml:"name_only" json:"nameOnly,omitempty"`
+ NewAbId *int `xml:"new_ab_id" json:"newAbId,omitempty"`
+ NewAccount *int `xml:"new_account" json:"newAccount,omitempty"`
+ NewCaste *string `xml:"new_caste" json:"newCaste,omitempty"`
+ NewEquipmentLevel *int `xml:"new_equipment_level" json:"newEquipmentLevel,omitempty"`
+ NewLeaderHfid *int `xml:"new_leader_hfid" json:"newLeaderHfid,omitempty" legend:"hf"`
+ NewRace *string `xml:"new_race" json:"newRace,omitempty"`
+ NewSiteCivId *int `xml:"new_site_civ_id" json:"newSiteCivId,omitempty" legend:"entity"`
+ OccasionId *int `xml:"occasion_id" json:"occasionId,omitempty"`
+ OldAbId *int `xml:"old_ab_id" json:"oldAbId,omitempty"`
+ OldAccount *int `xml:"old_account" json:"oldAccount,omitempty"`
+ OldCaste *string `xml:"old_caste" json:"oldCaste,omitempty"`
+ OldRace *string `xml:"old_race" json:"oldRace,omitempty"`
+ OverthrownHfid *int `xml:"overthrown_hfid" json:"overthrownHfid,omitempty" legend:"hf"`
+ PartialIncorporation *string `xml:"partial_incorporation" json:"partialIncorporation,omitempty"`
+ PersecutorEnid *int `xml:"persecutor_enid" json:"persecutorEnid,omitempty" legend:"entity"`
+ PersecutorHfid *int `xml:"persecutor_hfid" json:"persecutorHfid,omitempty" legend:"hf"`
+ PlotterHfid *int `xml:"plotter_hfid" json:"plotterHfid,omitempty" legend:"hf"`
+ PopFlid *int `xml:"pop_flid" json:"popFlid,omitempty"`
+ PopNumberMoved *int `xml:"pop_number_moved" json:"popNumberMoved,omitempty"`
+ PopRace *int `xml:"pop_race" json:"popRace,omitempty"`
+ PopSrid *int `xml:"pop_srid" json:"popSrid,omitempty"`
+ PosTakerHfid *int `xml:"pos_taker_hfid" json:"posTakerHfid,omitempty" legend:"hf"`
+ PositionId *int `xml:"position_id" json:"positionId,omitempty"`
+ PositionProfileId *int `xml:"position_profile_id" json:"positionProfileId,omitempty"`
+ PrisonMonths *int `xml:"prison_months" json:"prisonMonths,omitempty"`
+ ProductionZoneId *int `xml:"production_zone_id" json:"productionZoneId,omitempty"`
+ PromiseToHfid *int `xml:"promise_to_hfid" json:"promiseToHfid,omitempty" legend:"hf"`
+ PropertyConfiscatedFromHfid *[]int `xml:"property_confiscated_from_hfid" json:"propertyConfiscatedFromHfid,omitempty" legend:"hf"`
+ PurchasedUnowned *string `xml:"purchased_unowned" json:"purchasedUnowned,omitempty"`
+ Quality *int `xml:"quality" json:"quality,omitempty"`
+ Reason *string `xml:"reason" json:"reason,omitempty"`
+ ReasonId *int `xml:"reason_id" json:"reasonId,omitempty"`
+ RebuiltRuined *string `xml:"rebuilt_ruined" json:"rebuiltRuined,omitempty"`
+ ReceiverEntityId *int `xml:"receiver_entity_id" json:"receiverEntityId,omitempty" legend:"entity"`
+ ReceiverHistFigureId *int `xml:"receiver_hist_figure_id" json:"receiverHistFigureId,omitempty" legend:"hf"`
+ Relationship *string `xml:"relationship" json:"relationship,omitempty"`
+ RelevantEntityId *int `xml:"relevant_entity_id" json:"relevantEntityId,omitempty" legend:"entity"`
+ RelevantIdForMethod *int `xml:"relevant_id_for_method" json:"relevantIdForMethod,omitempty"`
+ RelevantPositionProfileId *int `xml:"relevant_position_profile_id" json:"relevantPositionProfileId,omitempty"`
+ ReligionId *int `xml:"religion_id" json:"religionId,omitempty"`
+ ResidentCivId *int `xml:"resident_civ_id" json:"residentCivId,omitempty" legend:"entity"`
+ Return *string `xml:"return" json:"return,omitempty"`
+ ScheduleId *int `xml:"schedule_id" json:"scheduleId,omitempty"`
+ SecretGoal *string `xml:"secret_goal" json:"secretGoal,omitempty"`
+ SeekerHfid *int `xml:"seeker_hfid" json:"seekerHfid,omitempty" legend:"hf"`
+ ShrineAmountDestroyed *int `xml:"shrine_amount_destroyed" json:"shrineAmountDestroyed,omitempty"`
+ SiteCivId *int `xml:"site_civ_id" json:"siteCivId,omitempty" legend:"entity"`
+ SiteEntityId *int `xml:"site_entity_id" json:"siteEntityId,omitempty" legend:"entity"`
+ SiteHfid *int `xml:"site_hfid" json:"siteHfid,omitempty" legend:"hf"`
+ SiteId *int `xml:"site_id" json:"siteId,omitempty" legend:"site"`
+ SiteId1 *int `xml:"site_id1" json:"siteId1,omitempty" legend:"site"`
+ SiteId2 *int `xml:"site_id2" json:"siteId2,omitempty" legend:"site"`
+ SiteId_1 *int `xml:"site_id_1" json:"siteId_1,omitempty" legend:"site"`
+ SiteId_2 *int `xml:"site_id_2" json:"siteId_2,omitempty" legend:"site"`
+ SitePropertyId *int `xml:"site_property_id" json:"sitePropertyId,omitempty"`
+ Situation *string `xml:"situation" json:"situation,omitempty"`
+ SlayerCaste *string `xml:"slayer_caste" json:"slayerCaste,omitempty"`
+ SlayerHfid *int `xml:"slayer_hfid" json:"slayerHfid,omitempty" legend:"hf"`
+ SlayerItemId *int `xml:"slayer_item_id" json:"slayerItemId,omitempty"`
+ SlayerRace *string `xml:"slayer_race" json:"slayerRace,omitempty"`
+ SlayerShooterItemId *int `xml:"slayer_shooter_item_id" json:"slayerShooterItemId,omitempty"`
+ SnatcherHfid *int `xml:"snatcher_hfid" json:"snatcherHfid,omitempty" legend:"hf"`
+ SourceEntityId *int `xml:"source_entity_id" json:"sourceEntityId,omitempty" legend:"entity"`
+ SourceSiteId *int `xml:"source_site_id" json:"sourceSiteId,omitempty" legend:"site"`
+ SourceStructureId *int `xml:"source_structure_id" json:"sourceStructureId,omitempty" legend:"structure"`
+ SpeakerHfid *int `xml:"speaker_hfid" json:"speakerHfid,omitempty" legend:"hf"`
+ State *string `xml:"state" json:"state,omitempty"`
+ StructureId *int `xml:"structure_id" json:"structureId,omitempty" legend:"structure"`
+ StudentHfid *int `xml:"student_hfid" json:"studentHfid,omitempty" legend:"hf"`
+ SubregionId *int `xml:"subregion_id" json:"subregionId,omitempty"`
+ Subtype *string `xml:"subtype" json:"subtype,omitempty"`
+ Successful *string `xml:"successful" json:"successful,omitempty"`
+ SurveiledContact *string `xml:"surveiled_contact" json:"surveiledContact,omitempty"`
+ SurveiledConvicted *string `xml:"surveiled_convicted" json:"surveiledConvicted,omitempty"`
+ TargetEnid *int `xml:"target_enid" json:"targetEnid,omitempty" legend:"entity"`
+ TargetHfid *int `xml:"target_hfid" json:"targetHfid,omitempty" legend:"hf"`
+ TargetIdentity *int `xml:"target_identity" json:"targetIdentity,omitempty"`
+ TargetSeenAs *string `xml:"target_seen_as" json:"targetSeenAs,omitempty"`
+ TeacherHfid *int `xml:"teacher_hfid" json:"teacherHfid,omitempty" legend:"hf"`
+ TopFacet *string `xml:"top_facet" json:"topFacet,omitempty"`
+ TopFacetModifier *int `xml:"top_facet_modifier" json:"topFacetModifier,omitempty"`
+ TopFacetRating *int `xml:"top_facet_rating" json:"topFacetRating,omitempty"`
+ TopRelationshipFactor *string `xml:"top_relationship_factor" json:"topRelationshipFactor,omitempty"`
+ TopRelationshipModifier *int `xml:"top_relationship_modifier" json:"topRelationshipModifier,omitempty"`
+ TopRelationshipRating *int `xml:"top_relationship_rating" json:"topRelationshipRating,omitempty"`
+ TopValue *string `xml:"top_value" json:"topValue,omitempty"`
+ TopValueModifier *int `xml:"top_value_modifier" json:"topValueModifier,omitempty"`
+ TopValueRating *int `xml:"top_value_rating" json:"topValueRating,omitempty"`
+ Topic *string `xml:"topic" json:"topic,omitempty"`
+ TraderEntityId *int `xml:"trader_entity_id" json:"traderEntityId,omitempty" legend:"entity"`
+ TraderHfid *int `xml:"trader_hfid" json:"traderHfid,omitempty" legend:"hf"`
+ TricksterHfid *int `xml:"trickster_hfid" json:"tricksterHfid,omitempty" legend:"hf"`
+ UnitId *int `xml:"unit_id" json:"unitId,omitempty"`
+ UnitType *string `xml:"unit_type" json:"unitType,omitempty"`
+ WantedAndRecognized *string `xml:"wanted_and_recognized" json:"wantedAndRecognized,omitempty"`
+ WcId *int `xml:"wc_id" json:"wcId,omitempty" legend:"wc"`
+ Wcid *int `xml:"wcid" json:"wcid,omitempty" legend:"wc"`
+ WinnerHfid *int `xml:"winner_hfid" json:"winnerHfid,omitempty" legend:"hf"`
+ WoundeeHfid *int `xml:"woundee_hfid" json:"woundeeHfid,omitempty" legend:"hf"`
+ WounderHfid *int `xml:"wounder_hfid" json:"wounderHfid,omitempty" legend:"hf"`
+ WrongfulConviction *string `xml:"wrongful_conviction" json:"wrongfulConviction,omitempty"`
+
+ OtherElements
+}
+
+func (r *HistoricalEvent) Id() int { return r.Id_ }
+func (r *HistoricalEvent) Name() string { return r.Type() }
+
+type EventObject struct {
+ Events []*HistoricalEvent `json:"events"`
+}
+
+func (r *EventObject) GetEvents() []*HistoricalEvent { return r.Events }
+func (r *EventObject) SetEvents(events []*HistoricalEvent) { r.Events = events }
+
+type HasEvents interface {
+ GetEvents() []*HistoricalEvent
+ SetEvents([]*HistoricalEvent)
+}
diff --git a/model/model.go b/model/model.go
new file mode 100644
index 0000000..a72d195
--- /dev/null
+++ b/model/model.go
@@ -0,0 +1,83 @@
+package model
+
+import "encoding/xml"
+
+type Region struct {
+ XMLName xml.Name `xml:"region" json:"-"`
+ NamedObject
+ Type string `xml:"type" json:"type"`
+}
+
+type UndergroundRegion struct {
+ XMLName xml.Name `xml:"underground_region" json:"-"`
+ NamedObject
+ Type string `xml:"type" json:"type"`
+}
+
+type Landmass struct {
+ XMLName xml.Name `xml:"landmass" json:"-"`
+ NamedObject
+}
+
+type Site struct {
+ XMLName xml.Name `xml:"site" json:"-"`
+ NamedObject
+ Type string `xml:"type" json:"type"`
+ Coords string `xml:"coords" json:"coords"`
+ Rectangle string `xml:"rectangle" json:"rectangle"`
+ Structures []Structure `xml:"structures>structure" json:"structures"`
+
+ EventObject
+}
+
+// func (obj Site) id() int { return obj.Id }
+// func (obj Site) name() string { return obj.Name }
+
+type Structure struct {
+ XMLName xml.Name `xml:"structure" json:"-"`
+ LocalId int `xml:"local_id" json:"localId"`
+ Name string `xml:"name" json:"name"`
+ Type string `xml:"type" json:"type"`
+}
+
+type WorldConstruction struct {
+ XMLName xml.Name `xml:"world_construction" json:"-"`
+ NamedObject
+}
+
+type Artifact struct {
+ XMLName xml.Name `xml:"artifact" json:"-"`
+ NamedObject
+ SiteId int `xml:"site_id" json:"siteId"`
+
+ EventObject
+}
+
+type HistoricalFigure struct {
+ XMLName xml.Name `xml:"historical_figure" json:"-"`
+ NamedObject
+ Race string `xml:"race" json:"race"`
+ Caste string `xml:"caste" json:"caste"`
+ OtherElements
+
+ EventObject
+}
+
+func (r *HistoricalFigure) Type() string { return "hf" }
+
+type HistoricalEventCollection struct {
+ XMLName xml.Name `xml:"historical_event_collection" json:"-"`
+ NamedObject
+ StartYear int `xml:"year"`
+ StartSeconds int `xml:"seconds72"`
+ EndYear int `xml:"end_year"`
+ EndSeconds int `xml:"end_seconds72"`
+ Type string `xml:"type" json:"type"`
+ EventIds []int `xml:"event" json:"eventIds"`
+}
+
+type Entity struct {
+ XMLName xml.Name `xml:"entity" json:"-"`
+ NamedObject
+ EventObject
+}
diff --git a/model/util.go b/model/util.go
new file mode 100644
index 0000000..cc6d217
--- /dev/null
+++ b/model/util.go
@@ -0,0 +1,50 @@
+package model
+
+import "encoding/xml"
+
+type NamedObject struct {
+ Id_ int `xml:"id" json:"id"`
+ Name_ string `xml:"name" json:"name"`
+}
+
+func (r *NamedObject) Id() int { return r.Id_ }
+func (r *NamedObject) Name() string { return r.Name_ }
+
+type Named interface {
+ Id() int
+ Name() string
+}
+
+type Identifiable interface {
+ Id() int
+}
+
+type TypedObject struct {
+ Type_ string `xml:"type" json:"type"`
+}
+
+func (r *TypedObject) Type() string { return r.Type_ }
+
+type Typed interface {
+ Type() string
+}
+
+type OtherElements struct {
+ Others_ []Element `xml:",any" json:"-"`
+}
+
+func (r *OtherElements) Others() []Element { return r.Others_ }
+
+type Others interface {
+ Others() []Element
+}
+
+type TypedOthers interface {
+ Type() string
+ Others() []Element
+}
+
+type Element struct {
+ XMLName xml.Name
+ Value string `xml:",innerxml"`
+}
diff --git a/model/world.go b/model/world.go
new file mode 100644
index 0000000..7075972
--- /dev/null
+++ b/model/world.go
@@ -0,0 +1,146 @@
+package model
+
+import (
+ "encoding/xml"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "reflect"
+)
+
+type World struct {
+ XMLName xml.Name `xml:"df_world"`
+ Name string `xml:"name"`
+ AltName string `xml:"altname"`
+
+ Regions []*Region `xml:"regions>region"`
+ UndergroundRegions []*UndergroundRegion `xml:"underground_regions>underground_region"`
+ Landmasses []*Landmass `xml:"landmasses>landmass"`
+ Sites []*Site `xml:"sites>site"`
+ WorldConstructions []*WorldConstruction `xml:"world_constructions>world_construction"`
+ Artifacts []*Artifact `xml:"artifacts>artifact"`
+ HistoricalFigures []*HistoricalFigure `xml:"historical_figures>historical_figure"`
+ HistoricalEvents []*HistoricalEvent `xml:"historical_events>historical_event"`
+ HistoricalEventCollections []*HistoricalEventCollection `xml:"historical_event_collections>historical_event_collection"`
+ Entities []*Entity `xml:"entities>entity"`
+
+ RegionMap map[int]*Region
+ UndergroundRegionMap map[int]*UndergroundRegion
+ LandmassMap map[int]*Landmass
+ SiteMap map[int]*Site
+ WorldConstructionMap map[int]*WorldConstruction
+ ArtifactMap map[int]*Artifact
+ HistoricalFigureMap map[int]*HistoricalFigure
+ HistoricalEventMap map[int]*HistoricalEvent
+ HistoricalEventCollectionMap map[int]*HistoricalEventCollection
+ EntityMap map[int]*Entity
+}
+
+func (w *World) Load(file string) {
+ xmlFile, err := os.Open(file)
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ fmt.Println("Successfully Opened users.xml")
+ defer xmlFile.Close()
+
+ byteValue, _ := ioutil.ReadAll(xmlFile)
+ fmt.Println(len(byteValue))
+
+ err = xml.Unmarshal(byteValue, w)
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println("World loaded")
+}
+
+func (w *World) Process() {
+ w.RegionMap = make(map[int]*Region)
+ mapObjects(&w.Regions, &w.RegionMap)
+
+ w.UndergroundRegionMap = make(map[int]*UndergroundRegion)
+ mapObjects(&w.UndergroundRegions, &w.UndergroundRegionMap)
+
+ w.LandmassMap = make(map[int]*Landmass)
+ mapObjects(&w.Landmasses, &w.LandmassMap)
+
+ w.SiteMap = make(map[int]*Site)
+ mapObjects(&w.Sites, &w.SiteMap)
+
+ w.WorldConstructionMap = make(map[int]*WorldConstruction)
+ mapObjects(&w.WorldConstructions, &w.WorldConstructionMap)
+
+ w.ArtifactMap = make(map[int]*Artifact)
+ mapObjects(&w.Artifacts, &w.ArtifactMap)
+
+ w.HistoricalFigureMap = make(map[int]*HistoricalFigure)
+ mapObjects(&w.HistoricalFigures, &w.HistoricalFigureMap)
+
+ w.HistoricalEventMap = make(map[int]*HistoricalEvent)
+ mapObjects(&w.HistoricalEvents, &w.HistoricalEventMap)
+
+ w.HistoricalEventCollectionMap = make(map[int]*HistoricalEventCollection)
+ mapObjects(&w.HistoricalEventCollections, &w.HistoricalEventCollectionMap)
+
+ w.EntityMap = make(map[int]*Entity)
+ mapObjects(&w.Entities, &w.EntityMap)
+
+ w.processEvents()
+}
+
+func (w *World) processEvents() {
+ legendFields := make(map[string][]int)
+
+ t := reflect.TypeOf(HistoricalEvent{})
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ l, ok := f.Tag.Lookup("legend")
+ if ok {
+ legendFields[l] = append(legendFields[l], i)
+ }
+ }
+
+ for eventIndex := 0; eventIndex < len(w.HistoricalEvents); eventIndex++ {
+ e := w.HistoricalEvents[eventIndex]
+ v := reflect.ValueOf(*e)
+ processEvent(e, &v, legendFields["entity"], &w.EntityMap)
+ processEvent(e, &v, legendFields["site"], &w.SiteMap)
+ processEvent(e, &v, legendFields["hf"], &w.HistoricalFigureMap)
+ processEvent(e, &v, legendFields["artifact"], &w.ArtifactMap)
+ // processEvent(e, &v, legendFields["wc"], &w.WorldConstructionMap)
+ // processEvent(e, &v, legendFields["structure"], &w.St)
+ }
+}
+
+func processEvent[T HasEvents](event *HistoricalEvent, v *reflect.Value, fields []int, objectMap *map[int]T) {
+ for _, i := range fields {
+ val := v.Field(i)
+ if !val.IsZero() {
+ switch val.Elem().Kind() {
+ case reflect.Slice:
+ ids := val.Interface().(*[]int)
+ for _, id := range *ids {
+ x, ok := (*objectMap)[id]
+ if ok {
+ x.SetEvents(append(x.GetEvents(), event))
+ }
+ }
+ case reflect.Int:
+ id := int(val.Elem().Int())
+ x, ok := (*objectMap)[id]
+ if ok {
+ x.SetEvents(append(x.GetEvents(), event))
+ }
+ default:
+ fmt.Println("unknown", val.Elem().Kind())
+ }
+ }
+ }
+}
+
+func mapObjects[T Identifiable](objects *[]T, objectMap *map[int]T) {
+ for i, obj := range *objects {
+ (*objectMap)[obj.Id()] = (*objects)[i]
+ }
+}
diff --git a/server/resource.go b/server/resource.go
new file mode 100644
index 0000000..b533de9
--- /dev/null
+++ b/server/resource.go
@@ -0,0 +1,43 @@
+package server
+
+import (
+ "encoding/json"
+ "fmt"
+ "legendsbrowser/model"
+ "net/http"
+ "strconv"
+
+ "github.com/gorilla/mux"
+)
+
+type Info struct {
+ Id int `json:"id"`
+ Name string `json:"name"`
+}
+
+func RegisterResource[T model.Named](router *mux.Router, resourceName string, resources map[int]T) {
+ list := func(w http.ResponseWriter, r *http.Request) {
+ values := make([]Info, 0, len(resources))
+ for _, v := range resources {
+ values = append(values, Info{Id: v.Id(), Name: v.Name()})
+
+ }
+ json.NewEncoder(w).Encode(values)
+ }
+
+ get := func(w http.ResponseWriter, r *http.Request) {
+ id, err := strconv.Atoi(mux.Vars(r)["id"])
+ if err != nil {
+ fmt.Println(err)
+ }
+
+ for _, item := range resources {
+ if item.Id() == id {
+ json.NewEncoder(w).Encode(item)
+ }
+ }
+ }
+
+ router.HandleFunc(fmt.Sprintf("/api/%s", resourceName), list).Methods("GET")
+ router.HandleFunc(fmt.Sprintf("/api/%s/{id}", resourceName), get).Methods("GET")
+}
diff --git a/server/servestatic.go b/server/servestatic.go
new file mode 100644
index 0000000..69f606b
--- /dev/null
+++ b/server/servestatic.go
@@ -0,0 +1,33 @@
+package server
+
+import (
+ "net/http"
+ "os"
+ "path/filepath"
+)
+
+type SpaHandler struct {
+ StaticPath string
+ IndexPath string
+}
+
+func (h SpaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ path, err := filepath.Abs(r.URL.Path)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ path = filepath.Join(h.StaticPath, path)
+
+ _, err = os.Stat(path)
+ if os.IsNotExist(err) {
+ http.ServeFile(w, r, filepath.Join(h.StaticPath, h.IndexPath))
+ return
+ } else if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+
+ http.FileServer(http.Dir(h.StaticPath)).ServeHTTP(w, r)
+}
diff --git a/util/util.go b/util/util.go
new file mode 100644
index 0000000..b74eefe
--- /dev/null
+++ b/util/util.go
@@ -0,0 +1,28 @@
+package util
+
+import "strings"
+
+func Keys[K comparable, V any](input map[K]V) []K {
+ keys := make([]K, 0, len(input))
+ for k := range input {
+ keys = append(keys, k)
+ }
+ return keys
+}
+
+func Values[K comparable, V any](input map[K]V) []V {
+ values := make([]V, 0, len(input))
+ for _, v := range input {
+ values = append(values, v)
+ }
+ return values
+}
+
+func ContainsAny(s string, substrings ...string) bool {
+ for _, substring := range substrings {
+ if strings.Contains(s, substring) {
+ return true
+ }
+ }
+ return false
+}