Updating character location based on occupied tile
All checks were successful
All checks were successful
This commit is contained in:
parent
6ca0f306bb
commit
e79f473ce4
@ -25,6 +25,10 @@ var _camera_start_offset := Vector3(0.0, 6.0, 10.0)
|
|||||||
var _border_material: StandardMaterial3D
|
var _border_material: StandardMaterial3D
|
||||||
var _known_locations: Dictionary = {}
|
var _known_locations: Dictionary = {}
|
||||||
var _locations_loaded := false
|
var _locations_loaded := false
|
||||||
|
var _character_id := ""
|
||||||
|
var _persisted_coord := Vector2i.ZERO
|
||||||
|
var _coord_sync_in_flight := false
|
||||||
|
var _queued_coord_sync: Variant = null
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@ -41,6 +45,8 @@ func _ready() -> void:
|
|||||||
|
|
||||||
var start_coord := SelectedCharacter.get_coord()
|
var start_coord := SelectedCharacter.get_coord()
|
||||||
_center_coord = Vector2i(roundi(start_coord.x), roundi(start_coord.y))
|
_center_coord = Vector2i(roundi(start_coord.x), roundi(start_coord.y))
|
||||||
|
_persisted_coord = _center_coord
|
||||||
|
_character_id = String(SelectedCharacter.character.get("id", SelectedCharacter.character.get("Id", ""))).strip_edges()
|
||||||
|
|
||||||
_block.visible = false
|
_block.visible = false
|
||||||
await _load_existing_locations()
|
await _load_existing_locations()
|
||||||
@ -58,6 +64,7 @@ func _process(_delta: float) -> void:
|
|||||||
return
|
return
|
||||||
_center_coord = target_coord
|
_center_coord = target_coord
|
||||||
_rebuild_tiles(_center_coord)
|
_rebuild_tiles(_center_coord)
|
||||||
|
_queue_coord_sync(_center_coord)
|
||||||
|
|
||||||
|
|
||||||
func _get_stream_position() -> Vector3:
|
func _get_stream_position() -> Vector3:
|
||||||
@ -204,8 +211,7 @@ func _load_existing_locations() -> void:
|
|||||||
_locations_loaded = false
|
_locations_loaded = false
|
||||||
_known_locations.clear()
|
_known_locations.clear()
|
||||||
|
|
||||||
var character_id := String(SelectedCharacter.character.get("id", SelectedCharacter.character.get("Id", ""))).strip_edges()
|
if _character_id.is_empty():
|
||||||
if character_id.is_empty():
|
|
||||||
push_warning("Selected character is missing an id; cannot load visible locations.")
|
push_warning("Selected character is missing an id; cannot load visible locations.")
|
||||||
_locations_loaded = true
|
_locations_loaded = true
|
||||||
return
|
return
|
||||||
@ -217,7 +223,7 @@ func _load_existing_locations() -> void:
|
|||||||
if not AuthState.access_token.is_empty():
|
if not AuthState.access_token.is_empty():
|
||||||
headers.append("Authorization: Bearer %s" % AuthState.access_token)
|
headers.append("Authorization: Bearer %s" % AuthState.access_token)
|
||||||
|
|
||||||
var err := request.request("%s/%s/visible-locations" % [CHARACTER_API_URL, character_id], headers, HTTPClient.METHOD_GET)
|
var err := request.request("%s/%s/visible-locations" % [CHARACTER_API_URL, _character_id], headers, HTTPClient.METHOD_GET)
|
||||||
if err != OK:
|
if err != OK:
|
||||||
push_warning("Failed to request visible locations: %s" % err)
|
push_warning("Failed to request visible locations: %s" % err)
|
||||||
request.queue_free()
|
request.queue_free()
|
||||||
@ -257,8 +263,45 @@ func _load_existing_locations() -> void:
|
|||||||
_known_locations[coord] = location_name
|
_known_locations[coord] = location_name
|
||||||
loaded_count += 1
|
loaded_count += 1
|
||||||
|
|
||||||
print("LocationLevel loaded %d visible locations for character %s." % [loaded_count, character_id])
|
print("LocationLevel loaded %d visible locations for character %s." % [loaded_count, _character_id])
|
||||||
if loaded_count == 0:
|
if loaded_count == 0:
|
||||||
push_warning("Visible locations request succeeded but returned 0 locations for character %s." % character_id)
|
push_warning("Visible locations request succeeded but returned 0 locations for character %s." % _character_id)
|
||||||
|
|
||||||
_locations_loaded = true
|
_locations_loaded = true
|
||||||
|
|
||||||
|
|
||||||
|
func _queue_coord_sync(coord: Vector2i) -> void:
|
||||||
|
if coord == _persisted_coord:
|
||||||
|
return
|
||||||
|
if _coord_sync_in_flight:
|
||||||
|
_queued_coord_sync = coord
|
||||||
|
return
|
||||||
|
_sync_character_coord(coord)
|
||||||
|
|
||||||
|
|
||||||
|
func _sync_character_coord(coord: Vector2i) -> void:
|
||||||
|
if _character_id.is_empty():
|
||||||
|
return
|
||||||
|
_coord_sync_in_flight = true
|
||||||
|
_queued_coord_sync = null
|
||||||
|
_sync_character_coord_async(coord)
|
||||||
|
|
||||||
|
|
||||||
|
func _sync_character_coord_async(coord: Vector2i) -> void:
|
||||||
|
var response := await CharacterService.update_character_coord(_character_id, coord)
|
||||||
|
if response.get("ok", false):
|
||||||
|
_persisted_coord = coord
|
||||||
|
SelectedCharacter.set_coord(coord)
|
||||||
|
else:
|
||||||
|
push_warning("Failed to persist character coord to %s,%s: status=%s error=%s body=%s" % [
|
||||||
|
coord.x,
|
||||||
|
coord.y,
|
||||||
|
response.get("status", "n/a"),
|
||||||
|
response.get("error", ""),
|
||||||
|
response.get("body", "")
|
||||||
|
])
|
||||||
|
|
||||||
|
_coord_sync_in_flight = false
|
||||||
|
if _queued_coord_sync != null and _queued_coord_sync is Vector2i and _queued_coord_sync != _persisted_coord:
|
||||||
|
var queued_coord: Vector2i = _queued_coord_sync
|
||||||
|
_sync_character_coord(queued_coord)
|
||||||
|
|||||||
@ -15,6 +15,16 @@ func delete_character(character_id: String) -> Dictionary:
|
|||||||
var url := "%s/%s" % [CHARACTER_API_URL, character_id]
|
var url := "%s/%s" % [CHARACTER_API_URL, character_id]
|
||||||
return await _request(HTTPClient.METHOD_DELETE, url)
|
return await _request(HTTPClient.METHOD_DELETE, url)
|
||||||
|
|
||||||
|
func update_character_coord(character_id: String, coord: Vector2i) -> Dictionary:
|
||||||
|
var url := "%s/%s/coord" % [CHARACTER_API_URL, character_id]
|
||||||
|
var payload := JSON.stringify({
|
||||||
|
"coord": {
|
||||||
|
"x": coord.x,
|
||||||
|
"y": coord.y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return await _request(HTTPClient.METHOD_PUT, url, payload)
|
||||||
|
|
||||||
func _request(method: int, url: String, body: String = "") -> Dictionary:
|
func _request(method: int, url: String, body: String = "") -> Dictionary:
|
||||||
var request := HTTPRequest.new()
|
var request := HTTPRequest.new()
|
||||||
add_child(request)
|
add_child(request)
|
||||||
|
|||||||
@ -14,3 +14,9 @@ func get_coord() -> Vector2:
|
|||||||
float(coord.get("x", 0)),
|
float(coord.get("x", 0)),
|
||||||
float(coord.get("y", 0))
|
float(coord.get("y", 0))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func set_coord(coord: Vector2i) -> void:
|
||||||
|
character["coord"] = {
|
||||||
|
"x": coord.x,
|
||||||
|
"y": coord.y
|
||||||
|
}
|
||||||
|
|||||||
@ -104,6 +104,31 @@ public class CharactersController : ControllerBase
|
|||||||
return Ok(locations);
|
return Ok(locations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPut("{id}/coord")]
|
||||||
|
[Authorize(Roles = "USER,SUPER")]
|
||||||
|
public async Task<IActionResult> UpdateCoord(string id, [FromBody] UpdateCharacterCoordRequest req)
|
||||||
|
{
|
||||||
|
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
|
if (string.IsNullOrWhiteSpace(userId))
|
||||||
|
return Unauthorized();
|
||||||
|
if (req.Coord is null)
|
||||||
|
return BadRequest("Coord required");
|
||||||
|
|
||||||
|
var allowAnyOwner = User.IsInRole("SUPER");
|
||||||
|
var character = await _characters.GetByIdAsync(id);
|
||||||
|
if (character is null)
|
||||||
|
return NotFound();
|
||||||
|
if (!allowAnyOwner && character.OwnerUserId != userId)
|
||||||
|
return Forbid();
|
||||||
|
|
||||||
|
character.Coord = req.Coord;
|
||||||
|
var updated = await _characters.UpdateCoordAsync(id, req.Coord);
|
||||||
|
if (!updated)
|
||||||
|
return NotFound();
|
||||||
|
|
||||||
|
return Ok(character);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[Authorize(Roles = "USER,SUPER")]
|
[Authorize(Roles = "USER,SUPER")]
|
||||||
public async Task<IActionResult> Delete(string id)
|
public async Task<IActionResult> Delete(string id)
|
||||||
|
|||||||
@ -10,6 +10,15 @@ Inbound JSON documents
|
|||||||
"name": "string"
|
"name": "string"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
- UpdateCharacterCoordRequest (`PUT /api/characters/{id}/coord`)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"coord": {
|
||||||
|
"x": "number",
|
||||||
|
"y": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Stored documents (MongoDB)
|
Stored documents (MongoDB)
|
||||||
- Character
|
- Character
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
namespace CharacterApi.Models;
|
||||||
|
|
||||||
|
public class UpdateCharacterCoordRequest
|
||||||
|
{
|
||||||
|
public required Coord Coord { get; set; }
|
||||||
|
}
|
||||||
@ -6,5 +6,6 @@ See `DOCUMENTS.md` for request payloads and stored document shapes.
|
|||||||
## Endpoints
|
## Endpoints
|
||||||
- `POST /api/characters` Create a character.
|
- `POST /api/characters` Create a character.
|
||||||
- `GET /api/characters` List characters for the current user.
|
- `GET /api/characters` List characters for the current user.
|
||||||
|
- `PUT /api/characters/{id}/coord` Update the current location coord for an owned character.
|
||||||
- `GET /api/characters/{id}/visible-locations` Ensure and list locations visible to that owned character.
|
- `GET /api/characters/{id}/visible-locations` Ensure and list locations visible to that owned character.
|
||||||
- `DELETE /api/characters/{id}` Delete a character owned by the current user.
|
- `DELETE /api/characters/{id}` Delete a character owned by the current user.
|
||||||
|
|||||||
@ -34,6 +34,14 @@ public class CharacterStore
|
|||||||
public async Task<Character?> GetByIdAsync(string id) =>
|
public async Task<Character?> GetByIdAsync(string id) =>
|
||||||
await _col.Find(c => c.Id == id).FirstOrDefaultAsync();
|
await _col.Find(c => c.Id == id).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
public async Task<bool> UpdateCoordAsync(string id, Coord coord)
|
||||||
|
{
|
||||||
|
var filter = Builders<Character>.Filter.Eq(c => c.Id, id);
|
||||||
|
var update = Builders<Character>.Update.Set(c => c.Coord, coord);
|
||||||
|
var result = await _col.UpdateOneAsync(filter, update);
|
||||||
|
return result.ModifiedCount > 0 || result.MatchedCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
public Task<List<VisibleLocation>> GetVisibleLocationsAsync(Character character)
|
public Task<List<VisibleLocation>> GetVisibleLocationsAsync(Character character)
|
||||||
{
|
{
|
||||||
return GetVisibleLocationsInternalAsync(character, ensureGenerated: false);
|
return GetVisibleLocationsInternalAsync(character, ensureGenerated: false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user