|
|
|
|
@ -1556,6 +1556,117 @@ func (h *AdminHandler) ForceLeaveTown(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
writeJSON(w, http.StatusOK, hero2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StartHeroRoadsideRest forces a hero into roadside rest at the current road position.
|
|
|
|
|
// POST /admin/heroes/{heroId}/start-roadside-rest
|
|
|
|
|
func (h *AdminHandler) StartHeroRoadsideRest(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
heroID, err := parseHeroID(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
|
|
|
"error": "invalid heroId: " + err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if h.isHeroInCombat(w, heroID) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hero, err := h.store.GetByID(r.Context(), heroID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
h.logger.Error("admin: get hero for start-roadside-rest", "hero_id", heroID, "error", err)
|
|
|
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load hero"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if hero == nil {
|
|
|
|
|
writeJSON(w, http.StatusNotFound, map[string]string{"error": "hero not found"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if hero.State == model.StateFighting || hero.State == model.StateDead || hero.HP <= 0 {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "hero must be alive and not in combat"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hm := h.engine.GetMovements(heroID); hm != nil {
|
|
|
|
|
out, ok := h.engine.ApplyAdminStartRoadsideRest(heroID)
|
|
|
|
|
if !ok || out == nil {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "cannot start roadside rest"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := h.store.Save(r.Context(), out); err != nil {
|
|
|
|
|
h.logger.Error("admin: save after start-roadside-rest", "hero_id", heroID, "error", err)
|
|
|
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to save hero"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
h.logger.Info("admin: start roadside rest", "hero_id", heroID)
|
|
|
|
|
h.writeAdminHeroDetail(w, out)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hero2, err := h.adminMovementOffline(r.Context(), hero, func(hm *game.HeroMovement, rg *game.RoadGraph, now time.Time) error {
|
|
|
|
|
if !hm.AdminStartRoadsideRest(now) {
|
|
|
|
|
return fmt.Errorf("cannot start roadside rest")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
h.logger.Info("admin: start roadside rest (offline)", "hero_id", heroID)
|
|
|
|
|
h.writeAdminHeroDetail(w, hero2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StopHeroRest exits a hero from non-town rest (roadside or adventure-inline) back to walking.
|
|
|
|
|
// POST /admin/heroes/{heroId}/stop-rest
|
|
|
|
|
func (h *AdminHandler) StopHeroRest(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
heroID, err := parseHeroID(r)
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{
|
|
|
|
|
"error": "invalid heroId: " + err.Error(),
|
|
|
|
|
})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hero, err := h.store.GetByID(r.Context(), heroID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
h.logger.Error("admin: get hero for stop-rest", "hero_id", heroID, "error", err)
|
|
|
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to load hero"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if hero == nil {
|
|
|
|
|
writeJSON(w, http.StatusNotFound, map[string]string{"error": "hero not found"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hm := h.engine.GetMovements(heroID); hm != nil {
|
|
|
|
|
out, ok := h.engine.ApplyAdminStopRest(heroID)
|
|
|
|
|
if !ok || out == nil {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "hero is not in roadside/adventure rest"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if err := h.store.Save(r.Context(), out); err != nil {
|
|
|
|
|
h.logger.Error("admin: save after stop-rest", "hero_id", heroID, "error", err)
|
|
|
|
|
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to save hero"})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
h.logger.Info("admin: stop rest", "hero_id", heroID)
|
|
|
|
|
h.writeAdminHeroDetail(w, out)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hero2, err := h.adminMovementOffline(r.Context(), hero, func(hm *game.HeroMovement, rg *game.RoadGraph, now time.Time) error {
|
|
|
|
|
if !hm.AdminStopRest(now) {
|
|
|
|
|
return fmt.Errorf("hero is not in roadside/adventure rest")
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
h.logger.Info("admin: stop rest (offline)", "hero_id", heroID)
|
|
|
|
|
h.writeAdminHeroDetail(w, hero2)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PauseTime freezes engine ticks, offline simulation, and blocks mutating game API calls.
|
|
|
|
|
// POST /admin/time/pause
|
|
|
|
|
func (h *AdminHandler) PauseTime(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|